JVM虚拟机在运行过程中包括以下几个运行区域:
1、线程私有区域:①程序计数器:当前线程所执行字节码指示器,通过计数器数值的改变来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、程序回复等基础功能均需依赖此计数器完成;②java虚拟机栈+本地方法栈:每个方法在执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口信息等
2、线程共有区域:①java堆:所有的对象实例以及数组都要在堆上分配,是垃圾收集管理的主要区域,亦可细分为新生代与老年带,新生代再细分为Eden,from survior,to survivor ②方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据。此区域的内存回收目标主要是针对常量池的回收和对类型的卸载
垃圾回收策略:
1、如何判断对象需要回收:①引用计数法:给对象添加一个引用计数器,每当一个地方引用它时计数器值便加1,当引用失效时则减1;当计数器为0时就是不再被使用,可以回收;但是目前主流的java虚拟机并未采用此方法,因为其并不能解决对象间互相循环依赖的情形;②可达性分析法:选取一些GC roots例如:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象,从这些节点开始往下搜索,当一个对象到GC roots没有任何引用链接时,证明此对象是不可用的
垃圾收集算法:
1、标记清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,此方法存在效率问题,标记和清除效率均不高,且标记清除会产生大量内存碎片,不利于之后程序运行过程中为大对象分配内存,导致垃圾回收提前进行。
2、复制算法:新生代回收算法,将新生代分为Eden,from survior,to survivor,hotspot默认Eden比Survivor大小比例为8:1;每当新生代进行垃圾手机时,会把Eden和from Survivor中还存在的对象都复制到to survivor,再把Eden和from Survivor清空,此时from 和to survivor互换角色,由于新生代对象大部分都是朝生夕死,因此此算法可以高效实现垃圾手机,并不会产生太多内存碎片。
3、标记-整理算法:复制算法在对象存活率较高时就要进行较多的复制操作,效率将降低,且to survivor将浪费较多内存空间,因此根据老年带的特点,采用标记-整理算法,即在标记后,将所有存活对象向一端移动,然后直接清理掉端边界外的所有对象,减少内存碎片
4、分代收集算法:即将内存根据对象存活周期分成不同的几块,一般把内存分为新生代,老年带,在两代采取不一样的垃圾收集算法。
垃圾收集算法:
Serial收集器:jdk1.3以前虚拟机新生代收集唯一选择,为单线程收集器,在进行垃圾收集时必须暂停其他所有的工作线程,直到GC结束。但其简单而高效,没有线程交互的开销,新生代采用复制算法,老年带采取标记-整理算法;
ParNew收集器:为Serial的多线程版本,其余例如控制参数(-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、回收策略均与Serial一致,能与CMS配合工作
Parallel Scanvenge收集器:新生代收集器,注重获得较高吞吐量,提供两个参数用于精准控制吞吐量,分别为最大垃圾收集停顿时间-XX:MaxGCPauseMillis以及直接设置吞吐量大小的-XX:gcTimeRatio;GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的
Serial Old收集器:用于给Client模式下虚拟机使用,主要两大用途:①在JDK1.5之后与Parallel Scanvenge收集器搭配使用;②作为CMS收集器的后备方案。
Parallel Old收集器:Parallel Scanvenge收集器的老年带版本,使用多线程和标记-整理算法,JDK1.6后提供
CMS收集器:JDK1.5后提供,以获取最短回收停顿时间为目标的收集器 ,基于标记-清除,整个收集、清理过程GC线程均可以与用户线程一起工作.CMS默认的启动线程数为(CPU数量+3)/4,可调整-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,+UseCMSCompactAtFullCollection用于CMS进行Full GC后进行内存碎片合并整理
G1(Garbage first)收集器:JDK1.7后提供,基于标记-整理,新生代和老年带不再物理隔离,都是一部分region的集合,它可以有计划的避免整个java堆进行全区域垃圾收集,化整为零将内存划分为许多region