到底谁是辣鸡?(对象是否存活和GC日志分析)

2024-05-31 11:58

本文主要是介绍到底谁是辣鸡?(对象是否存活和GC日志分析),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

垃圾收集要搞清楚的三件事(除了方法区回收其他都针对对象也就是堆区的回收)

垃圾收集(Carbage Collection,GC),垃圾收集需要考虑三件事:
1.哪些内存需要回收

方法计数器、虚拟机栈、本地方法栈三个区域随着线程生和灭,每一个栈帧所分配的内存在编译器大体上都是可知的,内存的回收和分配都具备确定性,所以不需要过多的考虑回收问题,因为方法结束或者是线程结束内存自动就会回收了。

方法区和堆区(垃圾收集区域需要回收)的内存分配则不一样,接口的多个实现、方法的多个分支占用的内存都可能不一样,只有在运行期才知道哪些对象会创建,这部分的内存回收和分配都是动态的,所以垃圾收集器关注的是这部分内存。

2.什么时候回收

对象死亡时回收(不被任何途径使用)。

3.如何回收

垃圾收集算法。

判断对象是否存活(什么时候回收和GC日志开启方式以及日志分析)

1.引用计数法

原理:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能被再使用的。

优点:实现简单、判定效率高。

缺点:对象间相互循环引用问题无法解决,
在下边的栗子里objA和objB的instance字段相互持有对方的引用,除此之外没有别的任何引用,而且两个对象已经不可能被访问,但是相互引用着对方,计数器都不是0,于是引用计数法无法告知GC去回收内存。

举个官方栗子:

public class GCObj{public Object instance = null;private static final int _1MB = 1024*1024;//判断对象的这部分内存是否会被回收,判断是不是使用的引用计数法private byte[] isGCByte = new byte[2*_1MB];public static void main(String[] args) {GCObj objA = new GCObj();GCObj objB = new GCObj();objA.instance = objB;objB.instance = objA;objA = null;objB = null;//假设在此GC,objA和objB是否会被回收System.gc();}
}

GC日志

[GC (System.gc()) [PSYoungGen: 7270K->664K(36864K)] 7270K->664K(121856K), 0.0181725 secs] [Times: user=0.02 sys=0.00, real=0.03 secs] [Full GC (System.gc()) [PSYoungGen: 664K->0K(36864K)] [ParOldGen: 0K->616K(84992K)] 664K->616K(121856K), [Metaspace: 3155K->3155K(1056768K)], 0.0915562 secs] [Times: user=0.11 sys=0.00, real=0.09 secs] 

观察日志可分析出(此处涉及到GC日志的分析,以后单独介绍),虚拟机并没有因为两个对象相互引用就不回收他们,这也说明虚拟机没有使用引用计数法判断对象存活。

GC日志分析:
首先如何让代码运行时打印GC日志:
日志分析

点击Edit Configurations…
设置VM optional参数

然后设置VM optinals参数:-XX:+PrintGCDetails
新生代老年代,GC,对内存分配

堆设置
-Xms :初始堆大小
-Xmx :最大堆大小
-XX:NewSize=n :设置年轻代大小
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n :设置持久代大小 收集器设置
-XX:+UseSerialGC :设置串行收集器
-XX:+UseParallelGC :设置并行收集器
-XX:+UseParalledlOldGC :设置并行年老代收集器
-XX:+UseConcMarkSweepGC :设置并发收集器 垃圾回收统计信息
-XX:+PrintHeapAtGC GC的heap详情
-XX:+PrintGCDetails GC详情
-XX:+PrintGCTimeStamps 打印GC时间信息
-XX:+PrintTenuringDistribution 打印年龄信息等
-XX:+HandlePromotionFailure 老年代分配担保(true or false) 并行收集器设置
-XX:ParallelGCThreads=n :设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n :设置并行收集最大暂停时间
-XX:GCTimeRatio=n :设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) 并发收集器设置
-XX:+CMSIncrementalMode :设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n :设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

更多设置参考:IDEA和Eclipse GC日志分析打印开启方法和参数设置

书上的GC日志
针对以上书中的例子来具体分析:
33.125 和 100.667 代表了GC发生的时间,这个数字代表从Java虚拟机启动以来经过的秒数。
[GC 和[Full GC 代表了GC的停顿类型并不是来区分GC发生在新生代还是老年代的,如果有Full说明本次GC发生了Stop The World (简称STW)的停顿。

ParNew收集器中的GC
ParNew收集器中的GC也出现Full,这一般是出现了担保失败之类的问题出现了STW
如果是调用System.gc()方法触发的GC,则如我们上边运行的程序中的那样,[GC (System.gc()) ,[Full GC (System.gc())

[DefNew(新生代)、[Tenured(老年代)、[Perm(永久代)表示GC发生的区域
在Serial收集器中显示[DefNew(Default New Generation)
ParNew收集器中显示[ParNew(Parallel New Generation)
Parallel Scavenge收集器中显示[PSYoungGen
老年代和永久带一致都是跟收集器决定的

后边方括号中的3324k->152k(3712k)含义是:“GC前该内存区域使用量->GC后该内存区域使用量(该内存区域总容量)”

方括号之外的3324k->152k(11904k)的含义是:“GC前Java堆的已使用量”->GC后Java堆的已使用量(Java堆的总容量)

再往后,0.0025925secs表示此次GC所占时间单位是秒,有的收集器(需要在VM optional中设置)可以显示更具体的时间数据,如:[Time:
user=0.01 sys=0.01,real=0.02 secs]这里边的 user、sys、real和Linux的time命令输出的时间含义一致分别代表:用户态消耗的CPU时间(user)、内核态消耗的CPU时间(sys)、操作从开始到结束所经过的墙钟时间(Wall Clock Time)(real)
墙钟时和CPU时间的区别:
墙钟时间包括各种非运算的等待耗时,例如磁盘IO、等待线程阻塞
而CPU时间则不包括这些耗时
但是当有多CPU或者多核时多线程操作会叠加这些CPU时间
所以有时user或者sys的事件会超过real的事件。

2.可达性分析算法

通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(就是从GC Roots 到这个对象是不可达),则证明此对象是不可用的。所以它们会被判定为可回收对象(例如图中的Obj5、Obj6、Obj7对象既是不可达的)。

可达性分析图

在Java语言中,可作为GC Roots对象的包括下面一下几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类能静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Native方法)引用的对象

在可达性分析算法中,要真正宣告一个对象死亡,至少要经历两次标记过程:

1.(缓刑)如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有 覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。

2.(对象自我拯救)如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。finalize()方法是对象逃脱死亡命运的最后一次机会,稍候GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将会被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

对象可以在被GC时自我拯救,这种自救机会只有一次,因为一个对象的finalize()方法最多只会被系统调用一次。

判断对象是否存活与“引用”有关(补充)

在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。

强引用(Strong Reference):就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

软引用(Soft Reference):用来描述一些还有用但并非必须的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。

弱引用(Weak Reference):用户描述非必须对象的。被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

虚引用(Phantom Reference):一个对象是否有虚引用存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用的唯一目的就是能在这个对象被收集器回收时刻得到一个系统通知。

方法区回收(扩展)

方法区(在Hotpot虚拟机中的永久代),垃圾收集效率要低于堆中对象的收集效率(70%-90%),永久代中回收的主要内容为两块:废弃常量和无用类

  • 废弃常量回收:以常量池中的字面量为例,如字符串常量“abc”,没有任何String对象引用常量池中的“abc”通俗点说就是没有任何String字符串叫“abc”,此时abc就会被清理出常量池,其他类(接口)、方法、字段的符号引用也是如此。
  • 无用类的回收:满足三个条件①该类的所有实例都已经被回收,也就是Java堆中不存在此类的任何实例②加载该类的ClassLoarder已经被回收③该对象的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。此三项只代表该类可以回收,要想真正回收类,还要在虚拟机参数中设置:-Xnoclassgc

参考:《深入理解Java虚拟机第二版:JVM高级特性与最佳实践》
http://blog.csdn.net/u014381710/article/details/48554465

这篇关于到底谁是辣鸡?(对象是否存活和GC日志分析)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017863

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

[职场] 公务员的利弊分析 #知识分享#经验分享#其他

公务员的利弊分析     公务员作为一种稳定的职业选择,一直备受人们的关注。然而,就像任何其他职业一样,公务员职位也有其利与弊。本文将对公务员的利弊进行分析,帮助读者更好地了解这一职业的特点。 利: 1. 稳定的职业:公务员职位通常具有较高的稳定性,一旦进入公务员队伍,往往可以享受到稳定的工作环境和薪资待遇。这对于那些追求稳定的人来说,是一个很大的优势。 2. 薪资福利优厚:公务员的薪资和

JavaScript全屏,监听页面是否全屏

在JavaScript中,直接监听浏览器是否进入全屏模式并不直接支持,因为全屏API主要是关于请求和退出全屏模式的,而没有直接的监听器可以告知页面何时进入或退出全屏模式。但是,你可以通过在你的代码中跟踪全屏状态的改变来模拟这个功能。 以下是一个基本的示例,展示了如何使用全屏API来请求全屏模式,并在请求成功或失败时更新一个状态变量: javascriptlet isInFullscreen =

高度内卷下,企业如何通过VOC(客户之声)做好竞争分析?

VOC,即客户之声,是一种通过收集和分析客户反馈、需求和期望,来洞察市场趋势和竞争对手动态的方法。在高度内卷的市场环境下,VOC不仅能够帮助企业了解客户的真实需求,还能为企业提供宝贵的竞争情报,助力企业在竞争中占据有利地位。 那么,企业该如何通过VOC(客户之声)做好竞争分析呢?深圳天行健企业管理咨询公司解析如下: 首先,要建立完善的VOC收集机制。这包括通过线上渠道(如社交媒体、官网留言

API-环境对象

学习目标: 掌握环境对象 学习内容: 环境对象作用 环境对象: 指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境。 作用: 弄清楚this的指向,可以让我们代码更简洁。 函数的调用方式不同,this指代的对象也不同。【谁调用,this就是谁】是判断this指向的粗略规则。直接调用函数,其实相当于是window.函数,所以this指代window。

PAT-1039 到底买不买(20)(字符串的使用)

题目描述 小红想买些珠子做一串自己喜欢的珠串。卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖。于是小红要你帮忙判断一下,某串珠子里是否包含了全部自己想要的珠子?如果是,那么告诉她有多少多余的珠子;如果不是,那么告诉她缺了多少珠子。为方便起见,我们用[0-9]、[a-z]、[A-Z]范围内的字符来表示颜色。例如,YrR8RrY是小红想做的珠串;那么ppRYYGrrYBR2258可以

Python分解多重列表对象,isinstance实现

“”“待打印的字符串列表:['ft','bt',['ad',['bm','dz','rc'],'mzd']]分析可知,该列表内既有字符对象,又有列表对象(Python允许列表对象不一致)现将所有字符依次打印并组成新的列表”“”a=['ft','bt',['ad',['bm','dz','rc'],'mzd']]x=[]def func(y):for i in y:if isinst

Sapphire开发日志 (十) 关于页面

关于页面 任务介绍 关于页面用户对我组工作量的展示。 实现效果 代码解释 首先封装一个子组件用于展示用户头像和名称。 const UserGrid = ({src,name,size,link,}: {src: any;name: any;size?: any;link?: any;}) => (<Box sx={{ display: "flex", flexDirecti

打包体积分析和优化

webpack分析工具:webpack-bundle-analyzer 1. 通过<script src="./vue.js"></script>方式引入vue、vuex、vue-router等包(CDN) // webpack.config.jsif(process.env.NODE_ENV==='production') {module.exports = {devtool: 'none

Java中的大数据处理与分析架构

Java中的大数据处理与分析架构 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来讨论Java中的大数据处理与分析架构。随着大数据时代的到来,海量数据的存储、处理和分析变得至关重要。Java作为一门广泛使用的编程语言,在大数据领域有着广泛的应用。本文将介绍Java在大数据处理和分析中的关键技术和架构设计。 大数据处理与