JVM虚拟机内存管理机制

Java虚拟机运行时管理的数据区域被称为“运行时数据区域”。该区域可以分为线程共享区域,包括
[list]
[*]方法区(Method Area):所有线程共享的内存区域,用于存放已被JVM加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。运行时常量池(Runtime Constant Pool)用于存放编译期生成的各种字面量和符号引用。
[*]堆(Heap):所有线程共享的内存区域,用于存放对象实例。
[/list]
和线程私有区域,包括
[list]
[*]虚拟机栈 (VM Stack):每个方法被调用时会创建栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。调用时入栈,结束时出栈。局部变量表存放了编译期可知的各种基本数据类型,局部变量表所需的内存在编译期间完成分配。
[*]本地方法栈(Native Method Stack):存储调用Native方法所需的各类信息。
[*]程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器
[/list]

[b][color=brown][size=medium]GC对如何划分内存空间?[/size][/color][/b]
GC的管理的主要区域是堆。现在GC基本采用分代收集算法,将堆细分为新生代(new generation)和老年代(tenured generation)。新生代又可以细分为Eden空间、From Survivor空间和To Survivor空间。堆还可以划分线程私有的分配缓冲区(Thread Local Allocation Buffer)。

[b][color=brown][size=medium]GC时如何判断对象无引用?[/size][/color][/b]
可以采用引用计数算法,但存在循环引用问题。目前主要的实现都采用根搜索算法(GC Roots Tracing)。通过查找对象实例到根是否存在可达路径,判断对象是否可GC。
GC Roots的对象包括:
[list]
[*]虚拟机栈(栈帧中局部变量表)中引用的对象
[*]方法区中类静态属性引用的对象
[*]方法区中的常量引用的对象
[*]本地方法栈中JNI(Native方法)引用的对象
[/list]

[b][color=brown][size=medium]GC如何回收方法区?[/size][/color][/b]
GC在方法区上的主要目标是针对常量池的回收和对类型的卸载。GC回收方法区包括两部分内容:废弃常量和无用的类。
废弃常量可以通过系统中是否存在该常量的引用进行判断。
一个类被判断为无用,必须同时满足以下三个条件:
[list]
[*]该类所有的实例都已经被回收,堆中不存在该类的任何实例
[*]加载该类的ClassLoader已经被回收
[*]该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类
[/list]

[b][color=brown][size=medium]GC算法[/size][/color][/b]
[list]
[*]标记-清除算法(Makr-Sweep):第一阶段,标记出所有需要回收的对象;第二阶段,统一清除所有被标记的对象。缺点:效率不高、会产生大量不连续的内存碎片。
[*]复制算法(Copying):将内存划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor空间。GC时将Eden和Survivor中存活的对象一次性复制到另一块目标Survivor空间上,然后清理Eden和之前用过的Survivor空间。Eden和Survivor默认比例是8:1,如果目标Survivor空间不够,可以依赖老年代内存进行分配担保(Handle Promotion)。缺点:空间浪费;如果存活率高,复制效率不高。
[*]标记整理算法(Mark-Compact):首先标记无用对象,然后将所有存活对象向一端移动,然后清理掉边界以外的内存。
[*]分代收集算法(Generational Collection):将堆分为新生代(对象存活率低)和老年代(对象存活率高),新生代采用复制GC,老年代采用标记-清理或者标记-整理算法。
[/list]

[b][color=brown][size=medium]GC实现[/size][/color][/b]
新生代垃圾收集器
[list]
[*]Serial:单线程收集器(复制算法)
[*]ParNew:Serial的多线程版本(复制算法)
[*]Parallel Scavenge:和ParNew类似,但可以控制CPU用于运行用户代码的时间与CPU总消耗时间的比值(复制算法)
[/list]

老年代垃圾收集器
[list]
[*]CMS(Concurent Mark Sweep):以获取最短回收停顿时间为目标的收集器。在互联网站或B/S系统的服务端上比较实用。GC分为四个步骤:初始标记(initial Mark)、并发标记(concurrent mark)、重新标记(remark)和并发清除(concurrent sweep)。缺点:对CPU资源敏感、浮动垃圾(并发清除过程中产生的垃圾)、内存碎片(需要额外碎片整理)。(标记-清除算法)
[*]Serial Old:单线程收集器(标记-整理算法)
[*]Parallel Old:Parallel Scavenge的老年代版本,在注重吞吐量及CPU资源敏感的场合,优先考虑Parallel Scavenge + Parallel Old。(标记-整理算法)
[/list]

新生代-老年代收集器:
[list]
[*]G1(Garbage First):标记-整理,无内存碎片;精确控制停顿;将堆(新生代和老年队)分为多个大小固定的分区,跟踪区域的垃圾堆积程度,根据允许的停顿时间,优先回收垃圾最多的区域。
[/list]

[b][color=brown][size=medium]内存分配策略[/size][/color][/b]
[list]
[*]对象优先在Eden分配
[*]大对象直接进入老年代
[*]长期存活的对象将进入老年代
[/list]

[b][color=brown][size=medium]Minor GC和Full GC[/size][/color][/b]
[list]
[*]新生代GC(Minor GC):发生在新生代的GC,非常频繁,速度很快
[*]老年代GC(Full GC):发生在老年代的GC,比Minor GC慢10倍以上。
[/list]

——总结自《深入理解Java虚拟机:JVM高级特性与最佳实践》第2章、第3章

你可能感兴趣的:(JAVA)