星球狂想战队|清华扫地僧带你深入JVM调优实战,思路清晰,精通简直太容易( 三 )

  • JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务 , 所以无论什么时候 , 总是有一块 Survivor 区域是空闲着的 。因此 , 新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间 。 新生代是 GC 收集垃圾的频繁区域 。
  • 当对象在 Eden ( 包括一个 Survivor 区域 , 这里假设是 from 区域 ) 出生后 , 在经过一次 Minor GC 后 , 如果对象还存活 , 并且能够被另外一块 Survivor 区域所容纳 ( 上面已经假设为 from 区域 , 这里应为 to 区域 , 即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ) , 则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中 , 然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ) , 并且将这些对象的年龄设置为1 , 以后对象在 Survivor 区每熬过一次 Minor GC , 就将对象的年龄 + 1 , 当对象的年龄达到某个值时 ( 默认是 15 岁 , 可以通过参数 -XX:MaxTenuringThreshold 来设定 ) , 这些对象就会成为老年代 。 但这也不是一定的 , 对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代 。
  • From Survivor区域与To Survivor区域是交替切换空间 , 在同一时间内两者中只有一个不为空 。
  • 永久代就是HotSpot虚拟机对虚拟机规范中方法区的一种实现方式 。 我们知道在HotSpot虚拟机中存在三种垃圾回收现象 , minor GC、major GC和full GC 。 对新生代进行垃圾回收叫做minor GC , 对老年代进行垃圾回收叫做major GC , 同时对新生代、老年代和永久代进行垃圾回收叫做full GC 。 许多major GC是由minor GC触发的 , 所以很难将这两种垃圾回收区分开 。 major GC和full GC通常是等价的 , 收集整个GC堆 。
    在1.8之后已经取消了永久代 , 改为元空间 , 类的元信息被存储在元空间中 。 元空间没有使用堆内存 , 而是与堆不相连的本地内存区域 。 所以 , 理论上系统可以使用的内存有多大 , 元空间就有多大 , 所以不会出现永久代存在时的内存溢出问题 。 这项改造也是有必要的 , 永久代的调优是很困难的 , 虽然可以设置永久代的大小 , 但是很难确定一个合适的大小 , 因为其中的影响因素很多 , 比如类数量的多少、常量数量的多少等 。 永久代中的元数据的位置也会随着一次full GC发生移动 , 比较消耗虚拟机性能 。 同时 , HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据 。 将元数据从永久代剥离出来 , 不仅实现了对元空间的无缝管理 , 还可以简化Full GC以及对以后的并发隔离类元数据等方面进行优化 。
    3.JVM的GC上面简单提到过 , 分别有三种回收现象:minor GC、major GC和full GC 。
    3.1 如何确定某个对象(垃圾)是可回收3.1.1 引用计数法给对象中添加一个引用计数器 , 每当有一个地方引用它时 , 计数器就加1;当引用失效时 , 计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的 。
    这种方式的问题是无法解决循环引用的问题 , 当两个对象循环引用时 , 就算把两个对象都设置为null , 因为他们的引用计数都不为0 , 这就会使他们永远不会被清除 。
    3.1.2 根搜索算法(可达性分析/可达算法)为了解决引用计数法的循环引用问题 , Java使用了可达性分析的方法 。 通过一系列的“GC roots”对象作为起点搜索 。 如果在“GC roots”和一个对象之间没有可达路径 , 则称该对象是不可达的 。 要注意的是 , 不可达对象不等价于可回收对象 , 不可达对象变为可回收对象至少要经过两次标记过程 。 两次标记后仍然是可回收对象 , 则将面临回收 。
    比较常见的将对象视为可回收对象的原因: