JVM GC之一找出不可达对象并回收
JAVA运行时数据区域
1、程序计数器:当前线程所执行的字节码的行号指示器。一个处理器只会执行一条线程中的指令,为了线程切换后能回复到正确的执行位置,所以每条线程都需要一个独立的计数器。各条线程之间互不影响,独立存储,属于‘线程私有’内存。
2、java虚拟机栈:描述的是JAVA方法执行的内存模型:每个方法执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法的被调用直至执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。所以也是线程私有的。
3、本地方法栈:和java虚拟机栈发挥的作用类似,只不过Java虚拟机栈是为JAVA方法服务的,而本地方法栈是为Native方法服务。所以也是线程私有的。
4、JAVA堆:JAVA堆是被所有线程共享的区域。所有的对象实例及数组都要在堆上分配。
5、方法区:是各个线程共享的内存区域。主要存储被虚拟机加载的类信息,常量、静态变量、编译后的字节码数据等。有一个别名:非堆。
6、运行时常量池:方法区的一部分,当然也是线程共享的咯。除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放各种字面量和符合引用。
7、直接内存:并不是虚拟机运行时的数据区的一部分。是在NIO中基于通道和缓冲区的I/O方式,使用Native函数库直接分配堆外内存。避免了JAVA堆和Native堆中来回复制数据。和(操作系统中内存页的用户空间和系统空间的虚拟映象类似)
=======================================
垃圾收集器
1、程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此内存的分配和回收都具备确定性。因此不需要考虑回收问题,因为线程结束或者方法结束,内存自然就回收了。而Java堆和方法去不一样,一个接口中的多个实现类需要的内存不一样,一个方法中的多个分支也不一样,只有在程序处于运行时才能知道创建哪些内存,所以这部分的内存的分配和回收都是动态的。
2、引用计数算法
引用计数:每当被引用时引用计数加1,有引用断开时引用计数减1.当引用计数为0时表示该对象可以被回收。JVM并不是通过引用计数算法来进行回收的,主要原因是很难解决对象之间的相互循环引用的问题。
3、根搜索算法:GC Roots
JVM是通过根搜索算法判定对象算法存活的。算法的基本思路是:通过一系列的名为GC Roots (GC 根节点)的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径,当一个对象到GC Roots没有任何引用链相连(图论说:从GC Roots到这个对象不可达)时, 证明此对象是不可用的。
4、可以作为GC Roots的对象
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈JNI中的引用的对象。
===========================================
对象Life or death
1、根搜索算法中不可达的对象也并非是‘非死不可’的,暂时是‘缓刑’阶段,要真正判断一个对象死亡要经历两次标记过程:如果对象在进行根搜索后发现对象不可达,那它将会进行被第一次标记并且进行筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机掉用过,这两种情况都视为‘没有必要执行’。
如果对象被认为有必要执行finalize()方法,那么这个方法会被放置在一个名为F-Queue的队列之中,并在稍后由一条由虚拟机自动建立的、低优先级的Finalizer线程去执行。这里的‘执行’也只是指虚拟机会触发这个方法,但并不承诺一定会执行。
finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC会对F-Queue中的对象进行第二次小规模的标记,如果对象在finalize()中重新与引用链上的任何一个对象建立了关联,就会被移出‘即将回收’集合,如果没有移出,那就真的离死亡不远了。
finalize()方法只会被系统自动调用一次。
============================================
回收方法区(HotSpot虚拟机中的永久代)
1、Java虚拟机规范中说过可以不要求虚拟机在方法区实行垃圾收集,在方法区进行垃圾回收的‘性价比’一般比较低。在堆中尤其是新生代中,常规的一次垃圾回收就可以回收70%--90%的空间,而永久代的垃圾收集效率远低于此。
2、永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。
3、如果字符串‘abc’进入了常量池,但是当前系统中没有任何String对象引用‘abc’常量,也没有其他地方引用了这个字面量,如果此时发生内存回收,而且必要的话 这个‘abc’常量就会被回收掉。
4、判断无用的类必须同时满足三个条件:
该类的所有实例都已经被回收,即JAVA堆中不存在该类的任何实例。
加载该类的ClassLoader已经被回收
加载该类的Class对象没有任何地方引用,而且不能通过反射访问。
5、在大量使用反射、动态代理、CGLib及动态生成JSP和频繁自定义ClassLoader的场景需要虚拟机自动卸载,以防止永久代不会溢出。
===================================================
垃圾收集算法:标记--清楚算法、复制算法、标记-整理算法、分代收集算法
1、标记--清除算法
最基础的算法,分为标记和清除两个阶段:
标记:首先标记出需要清除的对象,标记的过程就是上面的对象Life or death。之所以说是基础的算法,是因为后续的算法都是基于这个算法改进的。
该算法有两个缺点:效率和空间问题(其实计算机最纠结的无外乎这两个地方:时间和空间问题)。标记和清除的效率都不高。空间问题:标记清除之后会产生大量的碎片,可能会导致,当程序在以后的运行过程中需要分配较大的对象时无法找到足够的连续内存而不得不提前触发另一次收集动作。
2、复制算法
复制算法可以解决算法1的效率问题。将可用内存按容量划分成大小相等的两块,每次只使用其中的一块。当这一块的内存用完时,就吧还活着的对象复制到另一块上面,然后再把已使用的内存一次性清楚干净。这样使得每次都是对其中的一块内存回收,内存分配时也不用考虑内存碎片问题,只要移动堆顶指针即可。但是代价是内存缩小为原来的一半。
现在的商业虚拟机都采用这中算法来回收新生代,因为IBM研究表明新生代的的对象98%都是朝生夕死的,所以不需要按照1:1的比例来划分空间。
而是把内存分成一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor空间。当回收时,把Eden和Survivor中还活着的对象一次性拷贝到Survivor空间中,最后清理掉Eden和Survivor空间。HotSpot默认Eden和Survivor空间的大小是8:1,这样可以保证每次可用内存是90%(80%+10),只有10%是浪费的。
当Survivor空间不足时就要依赖老年代进行分配担保(就是说 有大头(老年代)在后面,才放心只留出10%来存放Eden和Survivor中活着的对象,万一Survivor不足时还有老年代在后面撑腰)。
3、标记-整理算法
复制算法在对象存活率较高时就要执行交多的复制操作,效率会变低。重点是如果不想浪费掉50%的空间就需要有额外的空间进行担保,以应对被使用的内存中有100%存活的极端情况,所以老年代一般不能直接使用这一算法。因为老年代没有其他的内存可担保了。
根据老年代的特定,提出了 ‘标记--整理’算法。原理是:标记过程仍然与‘标记--清除’算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有活着的对象都向一端移动,然后直接清理边界以外的内存。
4、分代算法
当前商业虚拟机都采用这种算法回收。没有什么新的思想,只是根据对象的存活周期把内存划分成几块,一般是新生代和老生代。这样可以根据各个年代的特定采用合适的算法进行垃圾收集。
在新生代中,对象的存活周期较短,朝生夕死,采用复制算法。只需要付出少量存活对象的复制成本就可以完成收集。
在老生代中,对象的存活率较高,没有额外的空间对它进行分配担保,必须使用‘标记--清除’或‘标记--整理’算法进行回收。
JVM GC之一找出不可达对象并回收的更多相关文章
- JVM GC 算法原理(转)
出处: https://mp.weixin.qq.com/s/IfUFuwn8dsvMIhTS3V01FA 对于JVM的垃圾收集(GC),这是一个作为Java开发者必须了解的内容,那么,我们需要去了解 ...
- JVM GC之垃圾收集器
简述 如果说收集算法时内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.这里我们讨论的垃圾收集器是基于JKD1.7之后的Hotspot虚拟机,这个虚拟机包含的所有收集器如图: Serial 收集 ...
- JVM&GC详解
1.JVM简介 JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器.它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java的字节码程序. ja ...
- JVM GC之对象生死
1.简述 在Java内存运行时区域的各个部分中,程序计数器.虚拟机栈.本地方法栈3个区域随着线程而生,随着线程而亡.栈中的栈帧随着方法的进入和退出而有条不紊的进行着入栈和出栈操作. 每个栈帧需要分配多 ...
- 02 JVM 从入门到实战 | 什么样的对象需要被 GC
引言 上一篇文章 JVM 基本介绍 我们了解了一些基本的 JVM 知识,本篇开始逐步学习垃圾回收,我们都知道既然叫垃圾回收,那回收的就应该是垃圾,可是我们怎么知道哪些对象是垃圾呢? 哪些对象需要被回收 ...
- jvm系列六、windows用jdk自带工具jps、jstack找出性能最差的代码
一.运行程序TestGC 二.用jps找出当前应用的进程号PID 到jdk安装目录的bin目录下输入: jps -l PID为1264 三.启动Process Explorer(下载地址:https: ...
- JVM调优之jstack找出最耗cpu的线程并定位代码
jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多.下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有 ...
- JVM调优之jstack找出最耗cpu的线程、定位代码
jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多.下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有 ...
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
随机推荐
- sql中NULL的问题
sql中NULL的问题 今天一不小心在sql中写出以下脚本 select defaultPositionId from TableName where UserId=1100528 and def ...
- 磁盘管理三-raid
前言:何为raid raid是利用多个磁盘组成一个可提升效能.可包含冗余的磁盘阵列组.常用于数据吞吐量大(视频),冗余要求高的场景 当前raid包含了raid0-7,以及组合方式raid10,raid ...
- opencv + numpy for python
OpenCV的全称是:Open Source Computer Vision Library.OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows和Mac OS ...
- "windows 正在启动"
xp 在cmd下使用了control userpasswords2 设定开机不需要密码之后,开机停留在“window正在启动”,无法进入.估计是设置的时候密码输入错误了( 并不会提示你错误:() 在停 ...
- 运行时数据区即内存分配管理——JVM之六
内存分配结构,请参考: http://iamzhongyong.iteye.com/blog/1333100
- Android创建和使用数据库详细指南(1)
http://database.51cto.com/art/200903/113334.htm 数据库支持每个应用程序无论大小的生命线,除非你的应用程序只处理简单的数据,那么就需要一个数据库系统存储你 ...
- FVANCOP/ChartNew.js
FVANCOP/ChartNew.js FVANCOP/ChartNew.js
- [置顶] Android开发实战记录(三)---HelloWorld
1.新建Android项目,选择Android Project,然后Next 2.填写项目名称HelloWorld然后next,这里注意下,Java开发的命名规范 3.选择Android SDK版本, ...
- hdu1753()模拟大型实景数字相加
标题信息: 手动求大的实数在一起, pid=1753">http://acm.hdu.edu.cn/showproblem.php?pid=1753 AC代码: /** *大实数相加 ...
- java学习笔记day04
1.static关键字 特点:1)随着类的加载而加载 2)优先于对象存在 3)被所有对象所共享 4)可以直接被类名调用(类名.静态成员) 注意:静态方法只能 ...