JVM学习笔记——自动内存管理
堆、栈、程序计数器
程序计数器:线程私有,程序计数器是为一个没有OOM错误的区域
栈:线程私有,线程所请求的栈深度大于虚拟机允许的深度,会抛出StackOverflowError异常;虚拟机没有足够的内存扩展时,会抛出OutOfMemoryError异常。
堆:线程共享,用于存放对象实例,GC作用与此,堆没有足够的内存扩展时,会抛出OutOfMemoryError异常。
Xss:JVM中的栈容量
Xms:JVM中的堆初始化容量
Xmx:JVM中的堆最大容量
Xmn:JVM中年轻代的容量
如java -Xmx2048m -Xms256m
,JVM以256M的堆内存启动,当堆内存不够时,扩展内存,最大可达2048M
JVM有许多不同的实现,其中HotSpot为Oracle的Java SE的主要实现。
新生代进入老年代
预定年龄
如果新生代经历一次GC没有被清理掉的话,那么它的年龄就加1岁,当到一定岁数(默认15岁)后,就可以进入老年代。
动态年龄
如果在Survivor空间中,所有相同年龄的对象所占空间达到Survivor空间的一半,那么以该年龄为阈值,大于等于该年龄的对象进入老年代。
对象存活分析
引用计数法
给对象添加一个引用计数器,每当有一个地方引用了该对象,引用数就加1;引用失效,引用数就减1。
引用计数法有一个缺点:循环引用的“孤岛”无法被GC。
主流的java虚拟机不使用该方法判断对象的死活。
可达性分析法
以“根节点”为出发点,可以将其他对象都串起来,构成一个引用链。若某个对象不在该引用链上,就被判断已死。
可作为“根结点”的有:1.栈中引用的对象;2.方法区中类静态属性引用的对象;3.方法区中常量引用的对象;4.本地方法栈中JNI引用的对象。
主流的java虚拟机使用可达性分析法判断对象的死活。
垃圾收集算法
对于已死的对象,我们应该对其进行垃圾收集。
标记-清除
该算法的最大缺点是标记清除后,会产生许多内存碎片。
复制
将内存划为多个区域,一次只用一个,启动垃圾收集的时候,将存活的对象都复制到新的内存块中,这样它们都是整齐的。
上图的1:1比例比较浪费内存空间。在实现中,一般是将内存分为1块较大的Eden空间和2块较小的Survivor空间(Eden空间:Survivor空间默认为8:1)。每次使用Eden和一块Survivor,另外一块Survivor空着。当启动垃圾收集时,将那两块中的存活对象复制到空着的Survivor块。如果空着的Survivor块放不下存活对象的话,会存放到老年代的内存中。
大多数JVM采用复制算法对新生代进行垃圾收集。
标记-整理
标记-整理算法结合了前两者,适用于老年代的垃圾收集。