JVM
- 线程隔离区:虚拟机栈(生命周期和线程相同),本地方法栈(执行native方法),程序计数器(当前线程所执行的字节码的行号指示器,执行native方法时为空)
- 共享区:栈(存放对象实例和数组),方法区(加载的类信息,静态变量,常量,即时编译后的代码)
垃圾收集算法:
- 标记清除:空间碎片过多,效率不高(G1,CMS)
- 复制算法:可用内存缩小,对象存活率高时效率低(Serial,ParNew,Parallel Scavenge)
- 标记整理:老年代算法
- 分代收集
GC Roots:全局性的引用(常量,类静态变量),执行上下文
- 虚拟机栈中引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
新生代和老年代的默认比例是1:2
新生代(Eden区(Java对象),ServivorFrom,ServivorTo)(8:1:1)
引用计数法,root搜索算法
长期存活的对象会进入老年区,默认为15
垃圾收集器:
-
新生代:Serial ,ParNew,Parallel Scavenge
-
老年代:CMS,Serial Old MSC,Parallel Old
-
Serial:新生代采用复制算法,暂停所有用户线程,(Client模式下新生代的默认收集器)
-
ParNew收集器:Serial多线程版本,采用复制算法(多线程)(Server模式下新生代收集器)
-
Parallel Scavenge:复制算法,并行多线程,达到可控制的吞吐量(CPU用于运行用户代码的时间与消耗的总时间的比值)
-
Serial Old:单线程,标记整理算法(Client模式下虚拟机使用,与Parallel Scavenge搭配使用)
-
Parallel Old:老年代收集器,多线程,标记整理算法
-
CMS(Concurrent Mark Sweep):最短停顿时间,老年代,标记清除算法,(初始标记stop the world,并发标记,重新标记stop the world,并发清除),对CPU资源敏感,无法处理浮动垃圾(并发清理阶段用户线程依然在运行),会产生大量碎片触发Full GC,-XX:CMSInitiatingOccupancyFraction=80(默认为92%)
- 初始标记:stop the world,标记存活的对象,标记年轻代中活着的对象引用到老年代的对象,为了加快此阶段处理速度,减少停顿时间,可以开启初始标记并行化,-XX:+CMSParallelInitialMarkEnabled,同时调大并行标记的线程数,线程数不要超过cpu的核数;
- 并发标记:从“初始标记”阶段标记的对象开始找出所有存活的对象;因为是并发运行的,在运行期间会发生新生代的对象晋升到老年代、或者是直接在老年代分配对象、或者更新老年代对象的引用关系等等,对于这些对象,都是需要进行重新标记的,否则有些对象就会被遗漏,发生漏标的情况。为了提高重新标记的效率,该阶段会把上述对象所在的Card标识为Dirty,后续只需扫描这些Dirty Card的对象,避免扫描整个老年代;并发标记阶段只负责将引用发生改变的Card标记为Dirty状态,不负责处理;
- 重新标记:stop the world完成标记整个年老代的所有的存活对象。 扫描年轻代和老年代中的对象,开启并行收集:-XX:+CMSParallelRemarkEnabled,可以先执行一次年轻代的GC
- 并发清除:清除那些没有标记的对象并且回收空间;
- 一般CMS的GC耗时 80%都在remark阶段,如果发现remark阶段停顿时间很长,可以尝试添加该参数:-XX:+CMSScavengeBeforeRemark,在执行remark操作之前先做一次ygc,目的在于减少ygen对oldgen的无效引用,降低remark时的开销,如果添加该参数后 ”ygc停顿时间+remark时间<添加该参数之前的remark时间“,说明该参数是有效的;
- 内存碎片:CMS是基于标记-清除算法的,只会将标记为为存活的对象删除,并不会移动对象整理内存空间,会造成内存碎片,-XX:CMSFullGCsBeforeCompaction=n
- G1收集器:并发与并行,分代收集,空间整合(整体上采用标记整理,局部采用复制算法),可预测的停顿(有计划的避免在整个Java堆中进行全区域的垃圾收集,通过维护优先列表,每次根据允许时间,回收价值最大的Region)(初始标记,并发标记,最终回收,筛选回收)