【0】README
0.1)本文部分文字转自:深入理解jvm,旨在学习 垃圾收集器与内存分配策略 的基础知识;
【1】垃圾回收概述
1)GC(Garbage Collection)需要完成的3件事情:哪些内容需要回收;什么时候回收;以及如何回收?
2)垃圾回收机制关注的内存是:java 堆 和 方法区,因为这部分内存的分配都是动态的;
【1.1】对象是否已死(判断)
1)可达性分析算法:通过java,C#等都是通过该算法来判断对象是否存活?。其基本思想就是通过一系列称为“GC Root”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链,但一个对象到GC Root没有任何引用链相连的话,则证明对象是不可用的;
2)java中,可作为GC Roots的对象包括下面几种(Objects):
O1)虚拟机栈中引用的对象;
O2)方法区中类静态属性引用的对象;
O3)方法区中常量引用的对象;
O4)本地方法栈中JNI(Native方法)引用的对象;
【1.2】java引用类型
1)引用分为: 强引用,软引用,弱引用,虚引用;以上四种引用强度逐渐减弱;
2)引用详解:
2.1)强引用:指在程序中普遍存在的,类似 Object obj = new Object();只要强引用存在,就不会被回收;
2.2)软引用:用来描述一些还有用但并非必须的对象;在系统将要发生内存溢出异常前,将会把这些对象列进回收范围中进行第二次回收;(SoftReference类来实现软引用)
2.3)弱引用:也是用来描述非必须对象的,但它的强度比软引用还要弱,被弱引用关联的对象只能生存到下一次GC之前;(WeakReference类来实现弱引用)
2.4)虚引用:它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成威胁,也无法通过一个虚引用获得一个对象实例。为一个对象设置虚引用的目的是: 能够在这个对象被回收时 收到一个系统通知。(PhantomReference 类来提供虚引用)
【1.3】生存还是死亡
1)即使在可达性分析算法中不可达的对象,而并非是“非死不可”的: 这时候他们暂时处于缓刑阶段,要真正宣告一个对象死亡,需要两次 标记marks;(干货——宣告一个对象死亡(被回收),需要两次标记marks)
mark1)如果对象在进行可达性分析后发现没有与 GC roots 相连接的引用链,那他将会被第一次标记并且进行一次筛选;筛选条件是此对象是否有必要执行 finalize方法。如果该对象没有覆盖 finalize方法,或者finalize方法已经被虚拟机调用过,虚拟机将这两种cases 都视为没有必要执行;
mark2)如果这个对象被判定有必要执行 finalize方法,那么这个对象将会放置到一个叫做F-Queue的队列中,并在稍后由一个虚拟机自动建立的,低优先级的finalizer 线程去执行它。

这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那它就真的离死不远了。(干货——finalize()方法是对象逃脱死亡命运的最后一次机会)

2)垃圾回收前的对象自救——荔枝演示
2.1)源代码
/*** 此代码演示了两点: * 1.对象可以在被GC时自我拯救。 * 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次* @author zzm*/
public class FinalizeEscapeGC {public static FinalizeEscapeGC SAVE_HOOK = null;public void isAlive() {System.out.println("yes, i am still alive :)");}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("finalize mehtod executed!");FinalizeEscapeGC.SAVE_HOOK = this; // System.gc()调用到了这里,拯救成功}public static void main(String[] args) throws Throwable {SAVE_HOOK = new FinalizeEscapeGC();//对象第一次成功拯救自己SAVE_HOOK = null;System.gc();// 因为Finalizer方法优先级很低,暂停0.5秒,以等待它Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}// 下面这段代码与上面的完全相同,但是这次自救却失败了SAVE_HOOK = null;System.gc();// 因为Finalizer方法优先级很低,暂停0.5秒,以等待它Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}}
}

2.2)打印结果
finalize mehtod executed!
yes, i am still alive :)
no, i am dead :(

3)不推荐使用 finalize方法(but try-finally)
笔者并不鼓励大家使用这种方法来拯救对象。相反,笔者建议大家尽量避免使用它,因为它不是C/C++中的析构函数,而是Java刚诞生时为了使C/C++程序员更容易接受它所做出的一个妥协。它的运行代价高昂,不确定性大,无法保证各个对象的调用顺序。finalize()能做的所有工作,使用try-finally或其他方式都可以做得更好、更及时,大家完全可以忘掉Java语言中还有这个方法的存在。
【1.4】回收方法区

1)永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果在这时候发生内存回收,而且必要的话,这个“abc”常量就会被系统“请”出常量池。

2)判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件(Conditions)才能算是“无用的类”:

C1)该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。

C2)加载该类的ClassLoader已经被回收。

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

3)是否对类进行回收: HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class及-XX:+TraceClassLoading、 -XX:+TraceClassUnLoading查看类的加载和卸载信息。

【2】垃圾收集算法
1)复制算法:

为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。(现在的商业虚拟机都采用这种收集算法来回收新生代)

现在的商业虚拟机都采用这种收集算法来回收新生代,IBM的专门研究表明:新生代中的对象98%是朝生夕死的,所以并不需要按照1∶1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor的空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8∶1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存是会被“浪费”的。(干货——注意这里的Eden 和 Survivor空间,后面的内容会用到)

2)分代收集算法:

当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象的存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

【2】垃圾收集器
  • 1)Serial收集器(单线程收集器)
  • 2)ParNew收集器(Serial收集器的多线程版本)
  • 3)Parallel Scavenge收集器: Parallel Scavenge收集器的目标: 则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。(干货——吞吐量的定义)
  • 4)Serial Old收集器(是Serial收集器的老年代版本)
  • 5)Parallel Old收集器(Parallel Scavenge收集器的老年代版本)
  • 6)CMS收集器(Concurrent Mark Sweep):目的是获取最短回收停顿时间,并发收集,低停顿;
  • 7)G1收集器(Garbage-First):可建立可预测的停顿时间模型,且面向server 的 应用;

【3】理解GC日志

1)阅读GC日志是处理jvm 内存问题的基础技能

2)看个荔枝:


对以上日志的分析(Analysis):

  • A1)33.125:代表GC发生的时间,表示从 jvm 启动以来经过的秒数;
  • A2)GC日志开头的 [GC 和 [FullGC 说明了这次垃圾收集的停顿类型: 如果是调用 System.gc() 方法进行收集,将显示 [Full GC(System);
  • A3)[DefNew,[Tenured,[Perm 表示: GC发生的区域,这里显示的区域名称与使用的GC收集器密切相关,如 DefNew == Default New Generation;
  • A4)后面方括号内部的 3324K->152K(3712K):表示 GC 前该内存区域已经使用容量->GC后该内存区域已使用容量(该内存区域的总容量);
  • A5)括号外的 3324K->152K(11904K): 表示GC前java 堆已使用容量->GC后堆已使用容量(java堆总容量);
  • A6)0.0031680 secs表示: 该内存区域进行GC 所占用的时间;
  • A7)有的收集器会给出具体的GC时间, 如[time: user=0.01 sys=0.00 real=0.02 secs],user, sys, real 分别代表用户态消耗的CPU时间, 内核态消耗的CPU事件 和 操作从开始到结束所经过的墙钟时间。

3)垃圾收集器参数总结



jvm(3)-垃圾收集器与内存分配策略相关推荐

  1. Java虚拟机(jvm)——垃圾收集器与内存分配策略

    文章目录 判断对象的存活 引用和对象死亡 4 种引用 宣告对象死亡 回收方法区 垃圾回收算法 标记清除算法: 标记复制算法: 标记整理算法 分代收集算法: 增量收集算法 一些相关的概念: System ...

  2. JVM篇·垃圾收集器与内存分配策略

    Java堆内存的整理方法 本文为<深入理解Java虚拟机_第三版 周志明>学习笔记 引用计数法 概念:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数 ...

  3. jvm垃圾收集器与内存分配策略

    2019独角兽企业重金招聘Python工程师标准>>> 垃圾收集器与内存分配策略: 以下参考周志明的<<深入理解jvm高级特性与最佳实践>>. 判断对象是否存 ...

  4. JVM:垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 1.对象已死吗 1).引用计数算法 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就 ...

  5. java eden分配参数,JVM垃圾收集器与内存分配策略,

    垃圾收集器与内存分配策略 对象存活判断 引用计数算法 给对象添加一个计数器,每有一个引用+1,当引用失效-1,若为0则不在被使用. 可达性分析算法 对象是否可到达GC roots 或者说GC root ...

  6. 垃圾收集器与内存分配策略(五)之垃圾日志与常见参数

    2019独角兽企业重金招聘Python工程师标准>>> 垃圾收集器与内存分配策略(五)--垃圾日志与常见参数 理解GC日志 每个收集器的日志格式都可以不一样,但各个每个收集器的日志都 ...

  7. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  8. java_opts gc回收器_jvm垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略: 以下参考周志明的<>. 判断对象是否存活: 引用计数:通过判断对象被引用的次数(为0,则表示不可被使用),但这很难解决对象相互循环引用的问题. 根搜索算法:即采 ...

  9. 7种垃圾收集器与内存分配策略,看这一篇就够了

    垃圾收集器与内存分配策略-垃圾收集器 (A).图中展示了7种不同分代的收集器: Serial.ParNew.Parallel Scavenge.Serial Old.Parallel Old.CMS. ...

最新文章

  1. ASP.NET Web API 实现客户端Basic(基本)认证 之简单实现
  2. 程序员如何坚持写作?
  3. VS查看C++类的内存分布、初步了解类的内存分布
  4. android avd 使用方法,Android中Android Virtual Device(AVD)使用教程
  5. Request.ServerVariables 参数大全
  6. Stackoverflow的见解:投票最多的是Spring 4问题
  7. cnckpi绩效考核模板_综合绩效审查模板
  8. jsp注册页面java代码_JSP 实现登录注册功能-Fun言
  9. 虚拟货币套利怎么处理
  10. 锻铸件三维扫描逆向设计,助力新产品研发
  11. 阿里云携手开源中国平台发百万悬赏项目
  12. CentOS6实验模板机搭建
  13. Excel切片器的使用
  14. 第33篇:DNS劫持攻击原理讲解及溯源分析的常规步骤
  15. 什么是项目管理中的质量控制?
  16. 分享李嘉诚的98个建议和忠告给年轻人
  17. USS-INT指令详解
  18. c语言约会,初次约会的十个话题 这么和女生聊天保准不冷场
  19. MySQL基础知识(1)
  20. 远程桌面窗口的字体异常的处理办法

热门文章

  1. 【NOIP2017】逛公园【最短路DAG】【dp】【拓扑排序】
  2. Math(牛客多校第三场)
  3. Early Orders
  4. 一般图带权多重匹配(欧拉图+最小费用流)
  5. [HNOI2012]集合选数(思维构造 + 状压dp)
  6. 8.11模拟:数据结构
  7. P3768-简单的数学题【莫比乌斯反演,杜教筛】
  8. POJ2018-Best Cow Fences【实数二分答案】
  9. 【矩阵乘法】Matrix Power Series(poj 3233)
  10. Java 并发总结——线程池