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

【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)垃圾收集器参数总结







本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防火墙例外里没有远程桌面_证明没有例外

防火墙例外里没有远程桌面您如何证明虚无的存在? 你应该? 在我编写的某些测试中,尤其是围绕验证或创建空对象的测试中,我真正想写的是这样的: assertThat( ... call some code ... ) .doesntThrow(); 您可以合理地编…

Java工程师必备技能

一、JavaSE (1)Java基础 1、数据结构 2、算法 3、包装类 4、字符串 5、异常处理 6、类与接口 7、泛型 8、集合 9、IO操作 10、序列化 11、迭代 12、枚举 13、properties 14、压缩 15、日期 (2)Java进阶 1、多线…

打破双亲委派么,怎么打破_打破了vs你错了

打破双亲委派么,怎么打破为微服务构建REST API时,需要做出一些关于响应的设计决策。 某些响应显然是微服务周围协议的产物-例如3xx代码之类的东西都与重定向和路由有关。 通常,您将尝试获取正确的2xx代码以取得成功。 如有疑问,将为200&…

java如何查看调用记录_查看Java记录

java如何查看调用记录JEP 359 (可在JDK 14中用作预览功能)将记录引入Java。 记录是对普通数据聚合建模的简单方法。 一个简单的范围记录如下所示: record Range( int from, int to) {} 记录定义实际上与具有以下内容的最终类相同&#xff1…

递归 反转字符串_使用递归反转字符串

递归 反转字符串1.简介 在本文中,您将学习如何使用递归方法来反转字符串。 第一个程序是反转字符串,第二个程序将读取用户的输入。 在之前的文章中,我已经展示了如何不使用任何内置函数来反转字符串,以及如何反转字符串中的单词 …

jvm(4)-虚拟机性能监控与故障处理工具

【0】README0.1)本文文字描述转自 “深入理解jvm”,旨在了解 虚拟机性能监控与故障处理工具的基础知识(仅仅在于了解); 【1】概述1)给一个系统定位问题的时候,知识,经验是关键基础&a…

代码大全和新月神话_神话般的代码

代码大全和新月神话考虑以下代码段: int max 10; int a 0; while (true) {// do a thing that may result in an early return if (a > max) {break;} }throw new RuntimeException("It ran out of attempts");上面有一些WTF。 循环有点不可思议&am…

Spring 思维导图,让 Spring 不再难懂(cache篇)

转载自: java思维导图 开源中国关于缓存缓存是实际工作中非常常用的一种提高性能的方法。而在java中,所谓缓存,就是将程序或系统经常要调用的对象存在内存中,再次调用时可以快速从内存中获取对象,不必再去创建新的重…

动手学习_动手选择值

动手学习由于冠状病毒的存在,可选的东西在空中,一切都变得可选,例如可选的公共聚会,可选的在家工作,可选的旅行等。 我现在是时候谈论处理NULL引用的软件工程中真正的“ 可选 ”了。 托尼霍尔(Tony Hoare…

JVM菜鸟进阶高手之路

本文转载自公众号 匠心零度问题现象 代码如下,使用 ParNew Serial Old 回收器组合与使用 ParNew CMS 回收器组合时,结果为什么差异如此之大 ?private static final int _1MB 1024 * 1024;public static void main(String[] args) throws …

事务的状态(状态模式)

【0】README0.1)本文部分文字描述转自 “head first设计模式”,旨在学习 事务的状态(状态模式) 的基础知识;【1】应用场景一1.1)还记得成都市各大高校内的米源自动售卖机吗?售卖机的主要制造商…

ogm neo4j_Neo4J OGM与Quarkus

ogm neo4j在下面的视频中,我演示了一个使用Neo4J数据库和Neo4J OGM的Quarkus应用程序示例。 看一下GitHub上的示例项目 。 我为咖啡豆创建了一个示例域,其中包含我们可以查询和匹配的某些风味配置文件。 红色节点是来自某个国家/地区的咖啡豆&#xff…

什么是ThreadLocal

本文转载自 占小狼的博客前言在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: “知道ThreadLocal吗?”“讲讲你对ThreadLocal的理解”…

Java bytecode instruction listings

【0】README 0.1)these contents are shiped from https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings Mnemonic Opcode (in hexadecimal) Opcode (in binary) Other bytes Stack [before]→[after] Description(no name)cb-fd these values ar…

为wmi执行例外_称之为例外?

为wmi执行例外虽然这是一个有关测试和Wiremock的Java示例,但它涉及一个更普遍的问题。 我们正在尝试重试Wiremock的verify方法,该方法可能会在我们要检查的端点被命中之前由测试调用。 在这种情况下,我们希望在几秒钟后循环尝试一次&#xf…

漫画:什么是MapReduce

转载自 脑洞有点大的 程序员小灰什么是MapReduce?MapReduce是一种编程模型,其理论来自Google公司发表的三篇论文(MapReduce,BigTable,GFS)之一,主要应用于海量数据的并行计算。MapReduce可以分…

orelse_可选的orElse vs orElseGet

orelseJava 8引入了一个很棒的Optional概念,我们在博客Java 8 Optional中进行了研究 。 因此,为了简要说明一下,Optional用于表示Optional对象或空值,而不是null引用。 这将有助于避免在我们尝试对空引用对象执行某些操作时发生空…

jvm(6)-java类文件结构(字节码文件)

【0】README0.1)本文部分文字描述转自 “深入理解jvm”,旨在学习类文件结构 的基础知识;0.2)本文荔枝以及荔枝的分析均为原创;0.3)下面的截图中有附注t*编号,不关乎博文内容;0.4&am…

你可能不知道的 10 条 SQL 技巧

转载自 58沈剑 开源中国 一、一些常见的SQL实践 (1)负向条件查询不能使用索引 select * from order where status!0 and stauts!1 not in/not exists都不是好习惯 可以优化为in查询: select * from order where status in(2,3) &#x…

夜神模拟器模拟安卓测试_使用模拟进行测试

夜神模拟器模拟安卓测试如果使用正确的方法,模拟对象将非常有用。 我在需要驱动软件开发使用的帖子中分享了一些使用Mock Objects的经验。 在这篇文章中,我分享了两件事 –使用模拟进行基于合同的测试。 –用于组织模拟代码的模式。 基于合同的测试 …