今天碰到了一个题目,讲的是关于堆内存的问题,题目如下
下面哪种情况会导致持久区jvm堆内存溢出?
A.循环上万次的字符串处理
B.在一段代码内申请上百M甚至上G的内存
C.使用CGLib技术直接操作字节码运行,生成大量的动态类
D.不断创建对象串行GC是在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式。
这个题目的答案选择C,我在做这个题之前对于JVM的了解几乎为0,所以看到这个题目基本就是随便选了一个,甚至没有看到原题中是持久区堆内存,只以为是内存溢出。所以在做完这个题目以后了解了一下JVM。
JVM是指Java虚拟机,JVM又分为了栈,堆,方法区,本地方法栈等几个部分,因为这个题目的缘故,重点了解了下堆。
1.栈
每一个线程执行的时候都会在栈申请开辟一块栈帧。每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果。
2.方法区
方法区用来存放要加载的类信息、静态变量、final类型的常量、属性和方法信息。
3,本地方法栈
本地方法栈用来支持native方法的执行,存储每个native方法的调用状态。
4.堆
代码中每new一个新对象,都会在堆中创建,然后下面重点了解了一下堆。
堆内存设置
堆分为两部分,Permanent和heap,Permanent Generation即持久带。
heap又分为Young Generation和Old Generation,即年轻代和年老代。
年轻代又分为Eden和Survivor。
Survivor又分为From和To。
年轻代
所有新创建的对象都放在新生代中,新生代的作用主要就是回收那些生命周期比较短的对象。
年老代
相对应的,老年代就存放那些生命周期比较长的对象。
这也就涉及到了垃圾回收机制。
因为年轻代中对象存活时间短,所以采用Copying算法,简单说就是把存活对象从一个区域拷贝到另一个区域,对应年轻代中就是Eden和From,To之间的拷贝,当触发GC进行回收依然空间不足就回进去年老代,年老代满了,进行Full GC,腾出的空间依然不足的话,就回出现内存溢出的错误OutOfMemory。比如开头写的那个题目中的ABD选项都是该原因导致的内存溢出。
而持久区则不同,持久区用于存放静态类型数据,如Java Class, Method 等。持久代对垃圾回收没有显著影响。但是有些应用可能动态生成或调用一些Class,例如前面题目的C选项,在这种时候往往需要设置一个比较大的持久代空间来存放这些运行过程中动态增加的类型。
最后简单记录垃圾回收的一点知识
并行回收GC是在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式。
并行GC要与旧生代的并发GC配合使用
旧生代的GC与新生代不同,对象存活的时间比较长,比较稳定,因此采用Mark算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。