1.  http://blog.csdn.net/column/details/14851.html   地址记录

2、关于Minor GC,Major GC与Full GC

1)  Minor GC:即新生代的GC,指发生在新生代的垃圾收集动作。当新生代的Eden区内存不足时,就会触发Minor GC。由于对象创建时,都会在Eden区分配内存,因此通过日志可以看到Minor GC动作执行相当频繁;同时,由于新生代对象朝生夕亡的特性,每次Minor GC的效果都十分理想。此外,Minor GC的效率也是非常高的。

2)Major GC与Full GC:我们可以认为Major GC与Full GC是一个概念,是指发生在老年代的引发了STW的GC。出现Full GC时,经常会伴随着至少一次的Minor GC,是由于老年代很多对象都会引用到新生代的对象,先进行一次Minor GC可以提高老年代GC的速度。一般Full GC会比Minor GC速度慢10倍以上。

3、垃圾收集算法以及垃圾收集器

3.1  如何判断对象存活以及永久代可回收的判断

在进行垃圾收集之前,需要做的一件事情就是判断对象对象是否存活。如何判断对象存活,一般有两种方法:

  1、引用计数法:引用计数法实现方式很简单,首先给对象添加一个引用计数器,每当有一个地方引用它时,计数器加1;当每有一个地方引用失效时,计数器减1;当到达零时,表示没有任何地方有对这个对象的引用了,该对象即为垃圾回收的目标对象。可是目前没有商业的JVM使用引用计数法去判断对象是否存活,原因是因为它有一个弊端,即当某两个对象存在循环引用时,引用计数器永远都不能到零,因此也不会通知垃圾收集器进行回收。

2、可达性分析算法:目前商业虚拟机的主流实现中,都通过该种方式判断对象是否可回收。主要思路是定义一系列叫做“GC Root”的根节点,从这些根节点往下搜索,走过的路径称为引用链,当一个对象到“GC Root”没有任何引用链相连的时候,则证明该对象是不可用的。如下图所示,obj5与obj6即为可回收的对象。由此就可以很好的解决循环引用的问题。

通过上面的方法即可以判断出新生代以及年老代的对象是否可以回收,但是还有一个比较特殊的区域,那就是永久代。永久代一样是需要进行垃圾回收的,但是在永久代中进行垃圾回收的效果可能会比新生代以及老年代差了许多。永久代中保存的主要是已被虚拟机加载的类信息以及常量信息,因此永久代中垃圾回收主要包括两个部分:废弃的常量和无用的类。

1、废弃的常量:判断常量是否已废弃的方法与判断对象是否存活的方式类似,即发现如果没有任何对象引用常量池中的常量时,即可断定该常量是可以被回收的。

2、无用的类:判断一个类是否无用会比较复杂一点,需要从以下几个方面进行判断:

  • 该类所有的实例已经被回收,也就是java堆中不存在该类的任何实例

  • 加载该类的ClassLoader已经被回收

  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射机制访问该类的方法

3.2 垃圾收集算法思想

1、复制算法: 现代商业虚拟机都使用这种方法来回收新生代。该算法的主要思路是:将内存分为两块,对象创建都在某一块上分配内存,当该块内存快满时触发垃圾回收,回收时将该块内存中存活的对象全部复制到另外一块内存中,然后将这块内存全部回收掉。这种方法的好处是由于是整块内存的回收,因此不会产生内存碎片。 现代商业虚拟机都是将内存分为三块,就是我们平时所说的1个Eden区以及2个Survivor区,由于新生代对象一般都是朝生夕亡(98%亡),所以一般默认比例8:1:1。垃圾回收时,将Eden区以及使用的1个Survivor区上存活的对象复制到另一个Survivor区上,然后清空Eden与之前的那个Survivor区。

2、标记清除算法:该算法的主要思路是:先标记出所有需要被回收的对象,在标记完成后统一回收被标记的对象,标记过程就是前面介绍的判断对象不存活了就标记。 该方法缺点也很明显,就是会产生大量的内存碎片,当有大对象进来时,可能总体空间是足够的,但是确找不到这样一块连续的内存空间而出现OOM异常。

3、标记整理/标记压缩算法: 该算法的主要思路是:先标记出所有需要被回收的对象,标记过程与上面一致,但是不是标记完就回收,而是让没有被标记的对象(存活的对象)全部向一端移动,最后清理掉边界以外的内存。该算法常被用来进行老年代的垃圾回收。

3.3 HotSpot虚拟机垃圾收集器

1、新生代收集器

  •  Serial收集器:最基本的,历史最悠久的收集器。单线程收集器,在进行垃圾回收时必须暂停掉所有的用户线程,即Stop The World。但是它也有一个优点就是简单高效。采用的是复制算法,通过-XX:+UseSerialGC配置。

  • ParNew收集器:其实就是Serial收集器的多线程版本,在单CPU的情况下效果不一定会比Serial好。但是他的优势是可以配合CMS收集器进行工作,采用的是复制算法。通过-XX:+UseParNewGc配置

  • Parallel Scavenge收集器:Parallel Scavenge也是一款多线程收集器,与ParNew的不同之处在于关注点不一样,其他收集器主要关注尽量降低STW的时间,而它主要关注在吞吐量,采用的是复制算法。通过-XX:+UseParallelGC配置

2、老年代收集器

  • Serial Old收集器:是Serial收集器的老年代版本,采用的是“标记整理/标记压缩算法”,通过-XX:+UseSerialOldGC配置

  • Parallel Old收集器:是Parallel Scavenge收集器的老年代版本,采用多线程以及“标记整理/标记压缩算法”。通过-XX:+UseParallelOldGC配置。

  • CMS 收集器:这时一款以获取最短回收停顿为目标的收集器,CMS是Concurrent Mark Sweep的缩写,从名字可以看到,这时一款使用“标记-清理”算法的并发收集器。主要分为:初始标记(CMS initial mark),并发标记(CMS concurrent mark),重新标记(CMS remark),并发清除(CMS concurrent sweep)4步,其中初始标记与重新标记两步仍然会导致‘Stop The World’,但是时间会比之前的收集器短许多。通过-XX:+UseConcMarkSweepGC配置

3、G1(GarbageFirst)收集器:当前收集器发展的最前沿的成果之一,能充分利用多CPU的硬件优势,来缩短STW的时间,可以不需要其他收集器的配合就可以管理整个堆内存,它最大的一个优势就是可预测停顿。

4、关于直接内存(Direct Memory)的GC

Direct Memory垃圾回收机制DirectByteBuffer所占内存是在堆内存之外的,因此一台机器堆内存分配的越多,会导致可用的堆外内存空间越少。Direct Memory的GC不能像新生代与老年代的GC一样,发现空间不足了就通知收集器进行垃圾回收,他只能等待老年代满了以后进行的Full GC顺便把他的废弃的内存回收掉,否则它只能等到抛出内存溢出的异常时,进入catch分支执行System.gc了,特别是如果设置了-XX:+DisableExplicit的话System.gc也会失效,注意,System.gc()执行的效果如下:

1
2
3
4
5
6
[Full GC [PSYoungGen: 0K->0K(306176K)] 
         [ParOldGen: 526K->526K(699392K)] 
         526K->526K(1005568K) 
         [PSPermGen: 2636K->2636K(262144K)]
0.0045990 secs] 
[Times: user=0.01 sys=0.00, real=0.00 secs]

也就是说,System.gc()执行的是一次Full GC。

下面证明Direct Memory是受GC控制的,例如ByteBuffer bb = ByteBuffer.allocateDirect(1024),这段代码的执行会在堆外占用1k的内存,Java堆内只会占用一个对象的指针引用的大小,堆外的这1k的空间只有当bb对象被回收时,才会被回收,这里会发现一个明显的不对称现象,就是堆外可能占用了很多,而堆内没占用多少,导致还没触发GC,那就很容易出现Direct Memory造成物理内存耗光。

  • 堆外内存的配置

堆外内存使用的配置如下:

  -XX:MaxDirectMemorySize=40M

下面通过实例来看下堆外内存的回收机制。

A、设置-verbose:gc -XX:+PrintGCDetails -XX:MaxDirectMemorySize=40M,执行如下代码:

1
2
3
 while (true) {
            ByteBuffer buffer = ByteBuffer.allocate(10 1024 1024);
        }

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[GC [PSYoungGen: 256266K->668K(306176K)] 256266K->676K(1005568K), 0.0019780 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 262030K->620K(306176K)] 262038K->636K(1005568K), 0.0011910 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 260143K->620K(306176K)] 260159K->636K(1005568K), 0.0014380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 257778K->588K(306176K)] 257794K->604K(1005568K), 0.0011710 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 257349K->572K(306176K)] 257365K->588K(1005568K), 0.0012910 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 257073K->588K(348672K)] 257089K->604K(1048064K), 0.0014530 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 338944K->32K(348672K)] 338960K->593K(1048064K), 0.0012450 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 338238K->32K(348672K)] 338799K->593K(1048064K), 0.0004050 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 338140K->32K(348672K)] 338701K->593K(1048064K), 0.0004250 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 338075K->32K(348672K)] 338636K->593K(1048064K), 0.0004440 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [PSYoungGen: 338033K->32K(348672K)] 338594K->593K(1048064K), 0.0004340 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 338005K->32K(348672K)] 338566K->593K(1048064K), 0.0004750 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 337987K->32K(348672K)] 338548K->593K(1048064K), 0.0004450 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 337975K->32K(348672K)] 338536K->593K(1048064K), 0.0004340 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 337967K->32K(348672K)] 338528K->593K(1048064K), 0.0004620 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [PSYoungGen: 337962K->32K(348672K)] 338523K->593K(1048064K), 0.0003790 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

运行这段代码会发现:程序可以一直运行下去,不会报OutOfMemoryError。如果使用了-verbose:gc -XX:+PrintGCDetails,会发现程序频繁的进行垃圾回收活动。

B、重新设置启动参数为:-verbose:gc -XX:+PrintGCDetails -XX:+DisableExplicitGC -XX:MaxDirectMemorySize=40M, 与之前的JVM启动参数相比,增加了-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC。代码如何显示调用GC呢,通过System.gc()函数调用。如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果,相当于是没有这行代码一样。执行同样代码,执行效果如下:

显然堆内存(包括新生代和老年代)内存很充足,但是堆外内存溢出了。也就是说NIO直接内存的回收,需要依赖于System.gc()。如果我们的应用中使用了java nio中的direct memory,那么使用-XX:+DisableExplicitGC一定要小心,存在潜在的内存泄露风险。

5、使用jstat命令查看线上堆内存以及GC情况

  • 命令格式:jstat -gcutil LVMID 间隔时间 执行次数( 通过jps命令可拿到LVMID)

说明:
  S0 表示Survivor0使用的比例,有时表示From,有时表示To

  S1 表示Survivor1使用的比例,有时表示From,有时表示To

  E 表示Eden区使用的比例

  O 表示Old区即老年代使用的比例

  P 表示Perm区即永久代或者方法区已使用的比例

  YGC 表示程序启动以来发生的Minor GC(Young GC)的总次数

  YGCT 表示Minor GC的总耗时

  FGC 表示程序启动以来发生的Full GC的总次数

  FGCT 表示Full GC的总耗时

  GCT 表示GC总耗时

附:jstat命令其他选项

1
2
3
4
5
6
7
8
9
-class           监视类装载、卸载数量,总空间以及装载类的耗时
-gc              监视整个java堆的状况,包括Eden区、两个Survivor区以及新生代与老年代的容量,已用空间以及GC总耗时等信息。
-gcutil          与上面gc类似,但是gcutil主要关注百分比
-gccause         与gcutil类似,但是增加了导致上次gc发生的原因
-gcnew           监视新生代GC的状况
-gcnewcapacity   与gcnew类似,输出主要关注使用到的最大、最小空间
-gcold           监视老年代GC的状况
-gcoldcapacity   与gcold类似,输出主要关注使用到的最大、最小空间
-gcpermcapacity  输出永久代使用到的最大、最小空间

GC知识随笔的更多相关文章

  1. 垃圾回收机制GC知识再总结兼谈如何用好GC(转)

    作者:Jeff Wong 出处:http://jeffwongishandsome.cnblogs.com/ 本文版权归作者和博客园共有,欢迎围观转载.转载时请您务必在文章明显位置给出原文链接,谢谢您 ...

  2. 垃圾回收机制GC知识再总结兼谈如何用好GC

    一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...

  3. JVM GC知识回顾

    这两天刚好有朋友问到我面试中GC相关问题应该怎么答,作为java面试中热门问题,其实没有什么标准回答.这篇文章结合自己之前的总结,对GC做一个回顾. 1.分代收集 当前主流VM垃圾收集都采用" ...

  4. GC知识记录

    2.关于Minor GC,Major GC与Full GC 1)  Minor GC:即新生代的GC,指发生在新生代的垃圾收集动作.当新生代的Eden区内存不足时,就会触发Minor GC.由于对象创 ...

  5. 垃圾回收机制GC知识再总结兼谈如何用好GC(其他信息: 内存不足)

    来源 图像操作,易内存泄露,边界像素 一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对 ...

  6. JVM内存布局及GC知识回顾

    注:本文篇幅较长,且需要有一定的java基础,建议各位看官,备好瓜子.饮料.小板凳,摆个让自己舒服的姿势,慢慢细看^_^, 文中所有素材,均来自互联网,本人只是详细梳理了一遍,形成此文. 一.JVM运 ...

  7. 记一起Java大对象引起的FullGC事件及GC知识梳理

    背景 最近发生了一起 Java 大对象引起的 FullGC 事件.记录一下. 有一位商家刷单,每单内有 50+ 商品.然后进行订单导出.订单导出每次会从订单详情服务取100条订单数据.由于 100 条 ...

  8. JVM内存布局及GC知识

    一.JVM运行时内存布局 按java 8虚拟机规范的原始表达:(jvm)Run-Time Data Areas, 暂时翻译为"jvm运行时内存布局". 从概念上大致分为6个(逻辑) ...

  9. 浅入 .NET Core 中的内存和GC知识

    目录 托管代码 自动内存管理 参考资料: [1]https://docs.microsoft.com/zh-cn/dotnet/standard/managed-code [2]:https://do ...

随机推荐

  1. Windows下基于Python3安装Ipython Notebook(即Jupyter)。python –m pip install XXX

    1.安装Python3.x,注意修改环境变量path(追加上python安装目录,如:D:\Program Files\Python\Python36-32) 2.查看当前安装的第三方包:python ...

  2. springboot不使用内置tomcat启动,用jetty或undertow

    Spring Boot启动程序通常使用Tomcat作为默认的嵌入式服务器.如果需要更改 - 您可以排除Tomcat依赖项并改为包含Jetty或Undertow: jetty配置: <depend ...

  3. spring MVC 后台token防重复提交解决方案

    看到公司有个部门提出了这个问题,补个粗略的解决方案... 1.编写拦截器 /** * Description: 防止重复提交 * * @Author liam * @Create Date: 2018 ...

  4. 在Linux环境下设置ArcGIS Server 服务开机自启

    在 VMware 11.0 中安装了CentOS 6.5的Linux系统中部署ArcGIS Server,安装完后默认开机不自动启动此服务,每次开机都要手动启动(如下图所示),这样太麻烦.本文记录了设 ...

  5. sencha 2.3中自己定义PullRefreshFn给PullRefresh加入下拉刷新事件

    Sencha removed the refreshFn from the pullrefresh plugin in ST 2.2. Here is an user extension with g ...

  6. Java 并发系列(一) ThreadPoolExecutor源码解析及理解

    ThreadPoolExecutor 它是线程池最核心的类, 这里对核心的方法做简要的剖析(会持续更新),以加深对线程池运行原理的理解. 1. 核心成员变量及相关方法 // ctl非常重要,用整型表示 ...

  7. OO——求导作业总结

    目录 OO--求导作业总结 程序结构的分析 第一次作业 第二次作业 第三次作业 对多项式合法性判断的讨论 程序bug的分析 未通过的互测bug bug的位置与程序结构的关系 继承和接口的使用 互测 手 ...

  8. java常用API之Date类

    Date类: 类 Date 表示特定的瞬间,精确到毫秒. 毫秒概念:1000毫秒=1秒 毫秒的0点: System.currentTimeMillis()  返回值long类型参数   用于获取当前日 ...

  9. 前端经典面试题:如何理解 HTML 语义化?

    本文最初于 2018-09-21 发布于 知乎 ,后在 <重学前端> 专栏的学习中,重新复习整理,发布于 Github 上,并计划写一系列前端学习相关的文章.欢迎 star . HTML ...

  10. day 87-1 Vue学习七之vue-cookie

      通过vue如何操作cookie呢 参考链接:https://www.jianshu.com/p/535b53989b39 第一步:安装vue-cookies npm install vue-coo ...