以栈或寄存器中的引用【GC Roots】为起点,找到堆中的对象,又从这些对象找到对堆中其余对象的引用,这种引用逐步扩展,最终以null引用或者基本类型结束,这样就造成了一颗以Java栈中引用所对应的对象为根节点的一颗对象树,若是栈中有多个引用,则最终会造成多颗对象树。
在这些对象树上的对象,都是当前系统运行所须要的对象,不能被垃圾回收。而其余剩余对象,则能够视为没法被引用到的对象,能够被当作垃圾进行回收。所以,垃圾回收的起点是一些根对象(java栈, 静态变量, 寄存器…)。而最简单的Java栈就是Java程序执行的main函数。这便是“标记-清除”的回收方式。java
GC Roots包括:算法
- 虚拟机栈中引用的对象。
- 方法区中类静态属性实体引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI引用的对象。
通常状况下,当新对象生成且在Eden申请空间失败时,就会触发Minor GC。
对Eden区域进行GC,清除非存活对象,而且把尚且存活的对象移动到Survivor区;而后整理Survivor的两个区。由于大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,因此Eden区的GC会频繁进行。因此通常在这里须要使用速度快、效率高的算法 - 复制,使Eden去能尽快空闲出来。数组
虚拟机给每一个对象定义了一个对象年龄(Age)计数器,能够经过参数 -XX:MaxTenuringThreshold (默认为 15 岁)来设置。
若是对象在 Eden 出生并通过第一次 Scavenge GC 后仍然存活,而且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Scavenge GC,年龄就增长 1 岁,当它的年龄增长到必定程度时,就会晋升到老年代中。对象晋升老年代的年龄阈值。服务器
对整个堆进行整理,包括Young、Tenured和MetaSpace。由于须要对整个对进行回收,因此很慢,所以应该尽量减小Full GC的次数。在对JVM调优的过程当中,很大一部分工做就是对于FullGC的调节。多线程
有以下缘由可能致使Full GC:并发
Major GC一般是跟full GC是等价的,收集整个GC堆。但由于HotSpot VM发展了这么多年,外界对各类名词的解读已经彻底混乱了,当说“major GC”的时候必定要问清楚他想要指的是上面的full GC仍是old gen。app
Parallel Scavenge(-XX:+UseParallelGC)框架下:
默认是在要触发full GC前先执行一次young GC,而且两次GC之间能让应用程序稍微运行一小下,以期下降full GC的暂停时间(由于young GC会尽可能清理了young gen的死对象,减小了full GC的工做量)。
以CMS GC (-XX:+UseConcMarkSweepGC)为例,它主要是定时去检查old gen的使用量,当使用量超过了触发比例(-XX:CMSInitiatingOccupancyFraction)就会启动一次CMS GC,对old gen作并发收集。经过配置-XX:+CMSScavengeBeforeRemark开启或关闭在CMS从新标记阶段以前的清除(YGC)尝试。框架
所谓"大对象" 就是指一个占用大量连续内存空间的对象。(如很长的字符串及数组)
若在Eden区 或 当前使用Survior区中存不下, 就须要把Eden区 或 当前使用Survior区的存活对象都移动到老年代中去,而后再将新对象存入Eden区。
一个大对象可以存入Eden区及 当前使用Survior区的几率比较小, 发生分配担保的几率比较大, 而分配担保须要涉及到大量的复制,就会形成效率低下。
所以: 大对象直接把放到老年代中去,从而就能避免大量的复制操做!
-XX:PretenureSizeThreshold 参数 该参数用于设置大小超过该参数的对象被认为是"大对象", 直接分配在老年代。(注意: 该参数只对Serial和ParNew收集器有效。)ide
为了使内存分配更加灵活,年龄相同对象的内存大小总和超过了任一Survivor空间的一半, 那么全部年龄相同及超过的对象都会被转移到老年代中,无须等到MaxTenuringThreshold要求的年龄。函数
分配担保是老年代为新生代做担保,担保有足够的空间存入新生代可存活的对象, 若是OldGeneration空间还不够就OOM。
发生MinorGC前, JVM首先会检查老年代中最大可用的连续空间是否大于新生代中全部对象的大小。若此条件:
在jdk5后,晋升不须要连续空间了。
In 5.0 we added the ability in the low pause collector to start a young generation collection and then to back out of it if there was not enough space in the tenured generation. Being able to backout of a young generation collection allowed us to make a couple of changes. We now keep an average of the amount of space that is used for promotions and use that (with some appropriate padding to be on the safe side) as the requirement for the space needed in the tenured generation. Additionally we no longer need a single contiguous chunk of space for the promotions so we look at the total amount of free space in the tenured generation in deciding if we can do a young generation collection. Not having to have a single contiguous chunk of space to support promotions is where fragmentation comes in (or rather where it doesn't come in as often).
此外,咱们再也不须要一个连续的空间大块晋升,因此咱们经过老年代的自由空间量总数决定是否能够作一个新生代的回收。没必要拥有一个连续的空间来支持晋升活动是兼容碎片化的状况https://blog.csdn.net/fei33423/article/details/70941113?utm_source=blogxgwz4
根据JVM的内存结构对堆进行分代收集:
在新生代中,每次垃圾收集都是有大批对象死去,少许存活; 且分Eden区+2个Survivor区。 比较适合复制算法。
而老年代中,对象存活率高,没有额外空间供其分配,就必须用“标记-清理-整理”算法。 常见如: CMS(Concurrent Mark-Sweep)以牺牲吞吐量为代价来得到最短回收停顿时间。对于要求服务器响应速度的应用上,这种垃圾回收器很是适合。在启动JVM参数加上-XX:+UseConcMarkSweepGC 表示对于老年代的回收采用CMS。其基础算法是:标记—清除。因此须要增长-XX:+UseCMSCompactAtFullCollection、-XX:CMSFullGCsBeforeCompaction来开启“整理”操做。
串行(Serial)收集: 使用单线程处理全部垃圾回收工做;
并行(ParNew)收集: 并行收集使用多线程处理垃圾回收工做,于是速度快,效率高。并且理论上CPU数目越多,越能体现出并行收集器的优点。
并发(Parallel)收集: 能够保证大部分工做都并发进行(应用不中止),垃圾回收只暂停不多的时间 。相对于串行收集和并行收集而言,前面两个在进行垃圾回收工做时,须要暂停整个运行环境,而只有垃圾回收程序在运行。所以,系统在垃圾回收时会有明显的暂停,并且暂停时间会由于堆越大而越长。(ps: 因此堆内存大小分配须要合理分片,并非越大越好)