java垃圾回收机制原理 ja

java中垃圾回收机制的原理

java中垃圾回收机制的原理
推荐一篇文章:
对高性能JAVA代码之内存管理
更甚者你写的代码,GC根本就回收不了,直接系统挂掉。GC是一段程序,不是智能,他只回收他认为的垃圾,而不是回收你认为的垃圾。
GC垃圾回收:
Grabage Collection相信学过JAVA的人都知道这个是什么意思。但是他是如何工作的呢?
首先,JVM在管理内存的时候对于变量的管理总是分新对象和老对象。新对象也就是开发者new出来的对象,但是由于生命周期短,那么他占用的内存并不是马上释放,而是被标记为老对象,这个时候该对象还是要存在一段时间。然后由JVM决定他是否是垃圾对象,并进行回收。
所以我们可以知道,垃圾内存并不是用完了马上就被释放,所以就会产生内存释放不及时的现象,从而降低了内存的使用。而当程序浩大的时候。这种现象更为明显,并且GC的工作也是需要消耗资源的。所以,也就会产生内存浪费。
JVM中的对象生命周期里谈内存回收:
对象的生命周期一般分为7个阶段:创建阶段,应用阶段,不可视阶段,不可到达阶段,可收集阶段,终结阶段,释放阶段。
创建阶段:首先大家看一下,如下两段代码:
test1:
for( int i=0; i《10000; i++)
Object obj=new Object();
test2:
Object obj=null;
for( int i=0; i《10000; i++)
obj=new Object();
这两段代码都是相同的功能,但是显然test2的性能要比test1性能要好,内存使用率要高,这是为什么呢?原因很简单,test1每次执行for循环都要创建一个Object的临时对象,但是这些临时对象由于JVM的GC不能马上销毁,所以他们还要存在很长时间,而test2则只是在内存中保存一份对象的引用,而不必创建大量新临时变量,从而降低了内存的使用。
另外不要对同一个对象初始化多次。例如:
public class A{
private Hashtable table = new Hashtable();
public A(){ table = new Hashtable();
// 这里应该去掉,因为table已经被初始化。
}
这样就new了两个Hashtable,但是却只使用了一个。另外一个则没有被引用。而被忽略掉。浪费了内存。并且由于进行了两次new操作。也影响了代码的执行速度。
应用阶段:即该对象至少有一个引用在维护他。
不可视阶段:即超出该变量的作用域。这里有一个很好的做法,因为JVM在GC的时候并不是马上进行回收,而是要判断对象是否被其他引用在维护。所以,这个时候如果我们在使用完一个对象以后对其obj=null或者obj.doSomething()操作,将其标记为空,可以帮助JVM及时发现这个垃圾对象。
不可到达阶段:就是在JVM中找不到对该对象的直接或者间接的引用。
可收集阶段,终结阶段,释放阶段:此为回收器发现该对象不可到达,finalize方法已经被执行,或者对象空间已被重用的时候。
JAVA的析构方法:
可能不会有人相信,JAVA有析构函数? 是的,有。因为JAVA所有类都继承至Object类,而finalize就是Object类的一个方法,这个方法在JAVA中就是类似于C++析构函数。一般来说可以通过重载finalize方法的形式才释放类中对象。如:
public class A{
public Object a;
public A(){ a = new Object ;}
protected void finalize() throws java.lang.Throwable{
a = null; // 标记为空,释放对象
super.finalize(); // 递归调用超类中的finalize方法。
}
}
当然,什么时候该方法被调用是由JVM来决定的。..。..。..。..。..。..。..。.
一般来说,我们需要创建一个destory的方法来显式的调用该方法。然后在finalize也对该方法进行调用,实现双保险的做法。
由于对象的创建是递归式的,也就是先调用超级类的构造,然后依次向下递归调用构造函数,所以应该避免在类的构造函数中初始化变量,这样可以避免不必要的创建对象造成不必要的内存消耗。当然这里也就看出来接口的优势。
数组的创建:
由于数组需要给定一个长度,所以在不确定数据数量的时候经常会创建过大,或过小的数组的现象。造成不必要的内存浪费,所以可以通过软引用的方式来告诉JVM及时回收该内存。(软引用,具体查资料)。
例如:
Object obj = new char[10000000000000000];
SoftReference ref = new SoftReference(obj);
共享静态存储空间:
我们都知道静态变量在程序运行期间其内存是共享的,因此有时候为了节约内存工件,将一些变量声明为静态变量确实可以起到节约内存空间的作用。但是由于静态变量生命周期很长,不易被系统回收,所以使用静态变量要合理,不能盲目的使用。以免适得其反。
因此建议在下面情况下使用:
1,变量所包含的对象体积较大,占用内存过多。
2,变量所包含对象生命周期较长。
3,变量所包含数据稳定。
4,该类的对象实例有对该变量所包含的对象的共享需求。(也就是说是否需要作为全局变量)。
对象重用与GC:
有的时候,如数据库操作对象,一般情况下我们都需要在各个不同模块间使用,所以这样的对象需要进行重用以提高性能。也有效的避免了反复创建对象引起的性能下降。
一般来说对象池是一个不错的注意。如下:
public abstarct class ObjectPool{
private Hashtable locked,unlocked;
private long expirationTime;
abstract Object create();
abstract void expire( Object o);
abstract void validate( Object o);
synchronized Object getObject(){。..};
synchronized void freeObject(Object o){。..};
这样我们就完成了一个对象池,我们可以将通过对应的方法来存取删除所需对象。来维护这快内存提高内存重用。
当然也可以通过调用System.gc()强制系统进行垃圾回收操作。当然这样的代价是需要消耗一些cpu资源。
不要提前创建对象:
尽量在需要的时候创建对象,重复的分配,构造对象可能会因为垃圾回收做额外的工作降低性能。
JVM内存参数调优:
强制内存回收对于系统自动的内存回收机制会产生负面影响,会加大系统自动回收的处理时间,所以应该尽量避免显式使用System.gc(),
JVM的设置可以提高系统的性能。例如:
java -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -Xms512m -Xmx512m
具体可以查看java帮助文档。我们主要介绍程序设计方面的性能提高。
JAVA程序设计中有关内存管理的其他经验:
根据JVM内存管理的工作原理,可以通过一些技巧和方式让JVM做GC处理时更加有效。,从而提高内存使用和缩短GC的执行时间。
1,尽早释放无用对象的引用。即在不使用对象的引用后设置为空,可以加速GC的工作。(当然如果是返回值。..。.)
2,尽量少用finalize函数,此函数是JAVA给程序员提供的一个释放对象或资源的机会,但是却会加大GC工作量。
3,如果需要使用到图片,可以使用soft应用类型,它可以尽可能将图片读入内存而不引起OutOfMemory.
4,注意集合数据类型的数据结构,往往数据结构越复杂,GC工作量更大,处理更复杂。
5,尽量避免在默认构造器(构造函数)中创建,初始化大量的对象。
6,尽量避免强制系统做垃圾回收。会增加系统做垃圾回收的最终时间降低性能。
7,尽量避免显式申请数组,如果不得不申请数组的话,要尽量准确估算数组大小。
8,如果在做远程方法调用。要尽量减少传递的对象大小。或者使用瞬间值避免不必要数据的传递。
9,尽量在合适的情况下使用对象池来提高系统性能减少内存开销,当然,对象池不能过于庞大,会适得其反.

java中垃圾回收机制的原理是什么

  1. GC的工作原理:引用计数,标记复制“引用计数“是一种简单但速度很慢的垃圾回收技术.所有对象都有一个引用计数器,当有引用连接时计数器加1,当引用离开作用域时或者被置于NULL时,计数器-1,垃圾回收器会在所有包含对象引用的列表上进行遍历,当发现某个对象的引用计数为0时,就释放占用的空间.“标记复制“的运行机制,垃圾回收器遍历包含所有引用的列表,当发现存活的对象引用时做上标记,这样当遍历完所有对象引用并做上标记的时候,执行垃圾回收,将没有标记的对象堆空间释放.

  2. 垃圾回收机制的优点:Java的垃圾回收机制使得java程序员不用担心内存空间的分配,减少了内存溢出.但同时也牺牲了一定的性能.

深入探索Java工作原理:JVM,内存回收及其他

  Java语言引入了Java虚拟机 具有跨平台运行的功能 能够很好地适应各种Web应用 同时 为了提高Java语言的性能和健壮性 还引入了如垃圾回收机制等新功能 通过这些改进让Java具有其独特的工作原理

   .Java虚拟机

  Java虚拟机(Java Virtual Machine JVM)是软件模拟的计算机 它可以在任何处理器上(无论是在计算机中还是在其他电子设备中)安全兼容地执行保存在 class文件中的字节码 Java虚拟机的 机器码 保存在 class文件中 有时也可以称之为字节码文件

  Java程序的跨平台特性主要是指字节码文件可以在任何具有Java虚拟机的计算机或者电子设备上运行 Java虚拟机中的Java解释器负责将字节码文件解释成为特定的机器码进行运行 因此在运行时 Java源程序需要通过编译器编译成为 class文件

  Java虚拟机的建立需要针对不同的软硬件平台来实现 既要考虑处理器的型号 也要考虑操作系统的种类 由此在SPARC结构 X 结构 MIPS和PPC等嵌入式处理芯片上 在UNIX Linux Windows和部分实时操作系统上都可实现Java虚拟机

   .无用内存自动回收机制

  在程序的执行过程中 部分内存在使用过后就处于废弃状态 如果不及时进行回收 很有可能会导致内存泄漏 进而引发系统崩溃 在C++语言中是由程序员进行内存回收的 程序员需要在编写程序时把不再使用的对象内存释放掉 这种人为管理内存释放的方法往往由于程序员的疏忽而致使内存无法回收 同时也增加了程序员的工作量 而在Java运行环境中 始终存在着一个系统级的线程 专门跟踪内存的使用情况 定期检测出不再使用的内存 并自动进行回收 避免了内存的泄露 也减轻了程序员的工作量

   .代码安全性检查机制

  安全和方便总是相对矛盾的 Java编程语言的出现使得客户端计算机可以方便地从网络上上传或下载Java程序到本地计算机上运行 但是如何保证该Java程序不携带病毒或者没有其他危险目的呢?为了确保Java程序执行的安全性 Java语言通过Applet程序来控制非法程序的安全性 也就是有了它才确保Java语言的生存

  Java字节码的执行需要经过以下 个步骤

  ( )由类装载器(class loader)负责把类文件( class文件)加载到Java虚拟机中 在此过程需要检验该类文件是否符合类文件规范

  ( )字节码校验器(bytecode verifier)检查该类文件的代码中是否存在着某些非法操作 例如Applet程序中写本地计算机文件系统的操作

  ( )如果字节码校验器检验通过 由Java解释器负责把该类文件解释成为机器码进行执行

  注意

  Java虚拟机采用 沙箱 运行模式 即把Java程序的代码和数据都限制在一定内存空间里执行 不允许程序访问该内存空间以外的内存 如果是Applet程序 还不允许访问客户端机器的文件系统

   Java的运行环境

  无论哪种语言都需要有它特定的运行环境 也就是平台 Java语言同样不例外 但是如何理解Java程序与硬件环境无关呢?

  几乎所有的语言都需要通过编译或者解释才可以被计算机执行 但是Java有一点不同 它同时需要这两个过程 其实 也正是因为这个原因才使Java这种语言具有了平台无关性 当完成一个Java源程序后 首先 通过Java翻译程序将它编译成一种叫做字节码的中间代码 然后再由Java平台的解释器将它转换成为机器语言来执行 这一平台的核心就是JVM

  Java的编译过程与其他的语言不同 像C++这样的语言 在编译时它是与计算机的硬件平台信息密不可分的 编译程序通过查表将所有指令的操作数和操作码等转换成内存的偏移量 即程序运行时的内存分配方式 目的是保证程序正常运行 而Java却是将指令转换成为一种 class的文件 这种文件不包含硬件的信息 需要执行时只要经过安装有JVM的机器进行解释 创建内存分配后再通过查表来确定一条指令所在的地址 这样就有效地保证了Java的可移植性和安全性

  Java平台具有这样的特性和它的结构有关 通常一个程序运行的平台是一个硬件或者软件运行的环境 目前比较流行的是Windows XP Linux Solaris和MacOS Java的平台不太一样 它由两个部分组成 即JVM和应用程序设计接口

   .JVM

  JVM是Java平台的核心 为了让编译产生的字节码能更好地解释与执行 因此把JVM分成了 个部分 JVM解释器 指令系统 寄存器 栈 存储区和碎片回收区

  ◆JVM解释器 即这个虚拟机处理字段码的CPU

  ◆JVM指令系统 该系统与计算机很相似 一条指令由操作码和操作数两部分组成 操作码为 位二进制数 主要是为了说明一条指令的功能 操作数可以根据需要而定 JVM有多达 种不同的操作指令

  ◆寄存器 JVM有自己的虚拟寄存器 这样就可以快速地与JVM的解释器进行数据交换 为了功能的需要 JVM设置了 个常用的 位寄存器 pc(程序计数器) optop(操作数栈顶指针) frame(当前执行环境指针)和vars(指向当前执行环境中第一个局部变量的指针)

  ◆JVM栈 指令执行时数据和信息存储的场所和控制中心 它提供给JVM解释器运算所需要的信息

  ◆存储区 JVM存储区用于存储编译过后的字节码等信息

  ◆碎片回收区 JVM碎片回收是指将使用过的Java类的具体实例从内存进行回收 这就使得开发人员免去了自己编程控制内存的麻烦和危险 随着JVM的不断升级 其碎片回收的技术和算法也更加合理 JVM 版后产生了一种叫分代收集技术 简单来说就是利用对象在程序中生存的时间划分成代 以此为标准进行碎片回收

   .Java应用程序设计接口

  Java Application Programming Interface简称Java API 其中文名为Java应用程序设计接口 它是一个软件集合 其中有许多开发时所需要的控件 可以用它来辅助开发

lishixinzhi/Article/program/Java/hx/201311/26733

JVM垃圾收集机制

JVM垃圾回收机制是java程序员必须要了解的知识,对于程序调优具有很大的帮助(同时也是大厂面试必问题)。

要了解垃圾回收机制,主要从三个方面:

(1)垃圾回收面向的对象是谁?

(2)垃圾回收算法有哪些?

(3)垃圾收集器有哪些?每个收集器有什么特点。

接下来一一讲解清楚:

一、垃圾回收面向的对象

也就是字面意思, 垃圾 回收嘛,重要的是垃圾,那什么对象是垃圾呢,简单来说就是无用的或已死的对象。这样又引申出来什么对象是已死的,怎么判断对象是否已死?

判断对象是否已死有两种算法和对象引用分类:

(1)引用计数算法:

也是字面意思,通过给对象添加引用计数器,添加引用+1,反之-1。当引用为0的时候,此时对象就可判断为无用的。

优点:实现简单,效率高。

缺点:无法解决循环引用(就是A引用B,B也引用A的情况)的问题。

(2)根搜索算法(也就是GC Roots):

通过一系列称为GC Roots的对象,向下搜索,路径为引用链,当某个对象无法向上搜索到GC Roots,也就是成为GC Roots不可达,则为无用对象。

如果一个对象是GC Roots不可达,则需要经过两次标记才会进行回收,第一次标记的时候,会判断是否需要执行finalize方法(没必要执行的情况:没实现finalize方法或者已经执行过)。如果需要执行finalize方法,则会放入一个回收队列中,对于回收队列中的对象,如果执行finalize方法之后,没法将对象重新跟GC Roots进行关联,则会进行回收。

很抽象,对吧,来一个明了的解释?

比如手机坏了(不可达对象),有钱不在乎就直接拿去回收(这就是没实现finalize方法),如果已经修过但是修不好了(已经执行过finalize方法),就直接拿去回收站回收掉。如果没修过,就会拿去维修店(回收队列)进行维修,实在维修不好了(执行了finalize方法,但是无法连上GC Roots),就会拿去回收站回收掉了。

那什么对象可以成为GC Roots呢?

1》虚拟机栈中的引用对象

2》本地方法栈中Native方法引用的对象

2》方法区静态属性引用对象

3》方法区常量引用对象

(3)对象引用分类

1》强引用:例如实例一个对象,就是即使内存不够用了,打死都不回收的那种。

2》软引用:有用非必须对象,内存够,则不进行回收,内存不够,则回收。例如A借钱给B,当A还有钱的时候,B可以先不还,A没钱了,B就必须还了。

3》弱引用:非必须对象,只能存活到下一次垃圾回收前。

4》虚引用:幽灵引用,必须跟引用队列配合使用,目的是回收前收到系统通知。

下面是java的引用类型结构图:

(1)软引用示例

内存够用的情况:

运行结果:

内存不够用的情况:

运行结果:

(2)弱引用示例结果:

无论如何都会被回收

(3)虚引用示例:

运行结果:

解释:为什么2和5的输出为null呢,如下

3为null是因为还没有进行gc,所以对象还没加入到引用队列中,在gc后就加入到了引用队列中,所以6有值。

这个虚引用在GC后会将对象放到引用队列中,所以可以在对象回收后做相应的操作,判断对象是否在引用队列中,可以进行后置通知,类似spring aop的后置通知。

二、垃圾回收发生的区域

垃圾回收主要发生在堆内存里面,而堆内存又细分为 年轻代 老年代 ,默认情况下年轻代和老年代比例为1:2,比如整个堆内存大小为3G,年轻代和老年代分别就是1G和2G,想要更改这个比例需要修改JVM参数-XX:NewRatio,

比如-XX:NewRatio=4,那老年代:年轻代=4:1。而年轻代又分为Eden区,S0(Survivor From)和S1(Survivor To)区,一般Eden:S0:S1=8:1:1,如果想要更改此比例,则修改JVM参数-XX:SurvivorRatio=4,此时就是Eden:S0:S1=4:1:1。

在年轻代发生GC称为Young GC,老年代发生GC成为Full GC,Young GC比Full GC频繁。

解析Young GC:

JVM启动后,第一次GC,就会把Eden区存活的对象移入S0区;第二次GC就是Eden区和S0一起GC,此时会把存活的对象移入S1区,S0清空;第三次GC就是Eden区和S1区进行GC,会把存活的对象移入S0区,如此往复循环15次(默认),就会把存活的对象存入老年区。

类似与如果有三个桶,编号分别为1(1号桶内的沙子是源源不断的,就像工地上。你们没去工地搬过砖可能不知道,但是我真的去工地上搬过啊),2,3。1里面装有沙子,需要将沙子筛为细沙。首先将桶1内的沙子筛选一遍过后的放置于桶2,第二次筛选就会将桶1和桶2里面的沙子一起筛,筛完之后放到桶3内,桶2清空。第三次筛选就会将桶1和桶3的沙子一起筛选,晒完放到桶2内,桶3清空。如此往复循环15次,桶2或桶3里面的沙子就是合格的沙子,就需要放到备用桶内以待使用。

上述中桶1就是Eden区,桶2就是S0区,桶3就是S1区。

三、垃圾回收算法

三种,分别是复制算法,标记-清除算法,标记-整理算法。

(1)复制算法。

其会将内存区域分成同样大小的两块,一块用来使用,另外一块在GC的时候存放存活的对象,然后将使用的一块清除。如此循环往复。

适用于新生代。

优点:没有内存碎片,缺点:只能使用一般的内存。

(2)标记-清除算法。

使用所有内存区域,在GC的时候会将需要回收的内存区域先进行标记,然后同意回收。

适用于老年代。

缺点:产生大量内存碎片,会直接导致大对象无法分配内存。

(3)标记-整理算法。

使用所有内存区域,在GC的时候会先将需要回收的内存区域进行标记,然后将存活对象忘一边移动,最后将清理掉边界以外的所有内存。

适用于老年代。

四、GC日志查看

利用JVM参数-XX:+PrintGCDetails就可以在GC的时候打印出GC日志。

年轻代GC日志:

老年代GC日志:

五、垃圾收集器

主要有四类收集器以及七大收集器

四类:

(1)Serial:单线程收集器,阻塞工作线程,它一工作,全部都得停下。

(2)Paralle:Serial的多线程版本,也是阻塞工作线程

(3)CMS(ConcMarkSweep):并行垃圾收集器,可以和工作线程一起工作。

(4)G1:将堆分成大小一致的区域,然后并发的对其进行垃圾回收。

怎么查看默认的收集器呢?

用JVM参数-XX:+PrintCommandLineFlags,运行之后会输出如下参数。可以看到,jdk1.8默认是Parallel收集器。

七大收集器:

(1)Serial:串行垃圾收集器,单线程收集器。用于新生代。用JVM参数-XX:+UseSerialGC开启,开启后Young区用Serial(底层复制算法),Old区用Serial Old(Serial的老年代版本,底层是标记整理算法)。

(2)ParNew:用于新生代,并行收集器。就是Serial的多线程版本。用JVM参数-XX:+UseParNewGC,young:parnew,复制算法。Old:serialOld,标记整理算法。-XX:ParallecGCThreads限制线程回收数量,默认跟cpu数目一样。只是新生代用并行,老年代用串行。

(3)Parallel Scavenge:并行回收收集器。用JVM参数-XX:+UseParallelGC开启,young:parallel scavenge(底层是复制算法),old:parallel old(parallel的老年代版本,底层是标记整理),新生代老年代都用并行回收器。

这个收集器有两个优点:

可控的吞吐量 :就是工作线程工作90%的时间,回收线程工作10%的时间,即是说有90%的吞吐量。

自适应调节策略 :会动态调节参数以获取最短的停顿时间。

(4)Parallel Old:Parallel Scavenge的老年代版本,用的是标记整理算法。用JVM参数-XX:+UseParallelOldGC开启,新生代用Parallel Scavenge,老年代用Parallel Old

(5)CMS(ConcMarkSweep):并发标记清除。 底层是标记清除算法,所以会产生内存碎片,同时也会耗cpu。 以获取最短回收停顿时间为目标。-XX:+UseConcMarkSweep,新生代用ParNew,老年代用CMS。CMS必须在堆内存用完之前进行清除,否则会失败,这时会调用SerialOld后备收集器。

初始标记和重新标记都会停止工作线程,并发标记和并发清除会跟工作线程一起工作。

(6)SerialOld:老年代串行收集器(以后Hotspot虚拟机会直接移除掉)。

(7)G1:G1垃圾收集器,算法是标记整理,不会产生内存碎片。横跨新生代老年代。实现尽量高吞吐量,满足回收停顿时间更短。

G1可以精确控制垃圾收集的停顿时间,用JVM参数-XX:MaxGCPauseMillis=n,n为停顿时间,单位为毫秒。

区域化内存划片Region,会把整个堆划分成同样大小的区域块(1MB~32MB),最多2048个内存区域块,所以能支持的最大内存为32*2048=65535MB,约为64G。

上图是收集前和收集后的对比,有些对象很大,分割之后就是连续的区域,也即是上图的Humongous。

上述理论可能有点乏味,下图很清晰明了(某度找的)。

下面来一张整个垃圾回收机制的思维导图(太大,分成两部分)。

=======================================================

我是Liusy,一个喜欢健身的程序猿。

欢迎关注【Liusy01】,一起交流Java技术及健身,获取更多干货。

java中GC是什么为什么要有GC

GC是垃圾回收的意思(gabage collection),内存处理器是编程人员容易出现问题的地方,忘记或者错误的内存回收导致程序或者系统的不稳定甚至崩溃,java的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,java语言没有提供释放已分配内存的俄显示操作方法。
希望能帮到你,谢谢!

【JVM】对象分配与回收–垃圾回收机制

对象回收需要确认三件事,那些需要回收(对象存活判定,二次标记),何时回收(GC触发条件)以及如何回收(垃圾回收算法,垃圾回收器)

1)引用计数法

2)可达性分析:GCRoots作为起始点,沿着引用链搜索。

GCRoots可以为:虚拟机栈中引用的对象;本地方法栈中native引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象;
1)强引用:即常见的那种引用,一个引用指向堆内存中对象。

2)软引用:还有用但非必须的,当空间不足了(即将OOM),才会对它进行回收。

3)弱引用:也是非必须的,不管空间剩余多少,只要有垃圾回收就进行回收。

4)虚引用:一个对象是否有虚引用,不会对它的生存事件有任何影响,也无法通过虚引用获得一个实例。唯一目的能在对象被回收时收到一个系统通知。
1)当对象被判定为不可达后,会进行一次标记,并筛选出覆盖了finalize方法且还没被执行过的对象进入下一步,那些没有覆盖的,或覆盖但已执行过的(finalize只能执行一次)将会被回收。

2)将筛选出的对象加入一个队列,并有一个优先级很低的Finalier线程去执行队列中对象的finalize方法,若finalize方法中该对象重新获得了引用,则复活,否则在第二次标记时他将被回收(第二次标记就是第一步中的标记,循环这两步)。
1)标记清除:将不可达的对象进行标记,GC时回收、—-》效率低、空间碎片

2)复制:将内存区域(堆)分为两块,每次只使用其中的一块。将可达对象进行标记,复制到另一边,然后将刚才那一边全部清空。

这中算法适用于存活对象很少的情况,经常被用于新生代的垃圾回收。

3)标记整理:将不可达对象标记后,清除对象后将存活对象向一端移动,减少空间碎片的产生。

该算法被广泛使用于老年代中。

4)分代收集:由于不同的对象有自己不同的特点。将区域划分为新生代与老年代。

新生代:对象大都朝生夕死,存活时间短。每次GC只有少量对象存活。一般用 复制算法 。(复制算法一般需要空间分配担保空间)

老年代:对象存活时间较长。也没有额外空间对他进行分配担保,更适合标记清除 、标记整理算法。
要注意垃圾回收器的设计目标是,有的是为了减少STW的时间,有的是为了吞吐量。这也应该作为一个选择回收器的标准。

1)Serial / Serial Old

Serial是一个单线程的垃圾回收器,进行垃圾回收时,必然要STW。Serial在新生代采用复制算法,其对应的老年代回收器在老年代采用标记整理法。

2)ParNew 

ParNew实质上一个多线程的Serial,他能够实现多个线程进行垃圾回收,但仍是需要用户线程STW之后才能并发执行垃圾回收。一般开启CPU核数个线程,用-XX:ParallelGCThreads设置。也可以用-XX:SurvivorRatio设置年轻代Eden与Survivor的比例,-XX:HandlerPromotionFailure设置空间分配担保是否开启。

是一种年轻代的垃圾回收器,采用复制算法。

3)Parallel Scavenge收集器 /Parallel Old

这是一款新生代收集器,也是采用复制算法。特点是关注点不在于停顿时间,而在于吞吐量。可通过时设置-XX:UseAdapatorSizePolicy为true将该收集器设为一个自适应调节策略,会自动根据吞吐量与停顿时间等因素进行自适应调节。

相关参数:设置吞吐量大小-XX: GCTimeRatio,-XX:MaxGCPauseMillis:设置最大垃圾收集停顿时间,-XX:UseAdaptativeSizePolicy自适应的设置新生代中区域的比例

Parallel Old是它的老年代的回收器,采用多线程和标记整理算法实现,可以选择Parallel Scavenge与Parallel Old配合使用组合为一个注重吞吐量的垃圾回收机制。

4)CMS (Concurrent Mark Sweep)老年代回收器!

由名字就可以看出来,是一款基于并发的标记清除算法的收集器,CMS收集器是针对于老年带的收集器,以最短停顿时间为目标。

STEP    分为四个步骤,其中耗时最长的并发标记与并发清除是与用户线程并发完成的,而其它两个步骤会有短暂的停顿,以此种方式尽量减少停顿时间

a.初始标记:就是标记一些GCRoots的直接引用,速度非常快,这一过程会有短暂的SWT

b.并发标记:与用户线程并发的进行GCRoots tracing,沿引用链去标记不可达的对象。

c.重新标记:由于第二步是与用户线程一起发生的,有可能在回收过程中会有新的垃圾产生,这一步就是对于这些浮动垃圾进行标记(短暂停顿)

d.并发清除:与用户线程并发进行垃圾回收,采用标记清除算法。

优缺点

并发收集、低停顿

a.对CPU资源敏感,降低吞吐量(吞吐量指用户线程占用时间/总运行时间,这种算法占用了用户现成的一部分CPU时间)(并发涉及的程序都会争抢CPU资源)

b.标记清除算法导致的空间碎片:可能会造成大量空间浪费,若大对象(直接进入老年代)存储时,则会由于连续空间不足引发FullGC。可通过设置-XX:UseCMSCompactAtFullCollection开关,设置是否在FullGC前执行碎片整理合并。

c.浮动垃圾:在并发阶段,用户也可能会随时产生垃圾。在这里需要考虑另一个问题,由于是并发的,所以我们需要考虑用户线程需要占用一部分存储空间,就不可能等到老年代快满了再回收垃圾,需预留给用户线程一些空间。可以通过设置-XX:CMSInitialingOccupationFraction来设置触发FullGC时内存空间使用比例,若这个参数过低,则会增加发生GC的次数;若过高,留给用户空间的内存不够,又会引发Concurrent Mode Failure,而启用Serial Old收集器作为备份方案。

5)G1(Garbage first)

特点

a.将新生代与年老代同一划分为多个Region区域。

b.标记整理算法。

c.与用户线程并发执行。

d.可预测停顿时间。因为可以有计划的避免在整个java堆中进行全局的回收,将堆划分为多个Region,并为每个计算Region里垃圾堆积的价值大小(回收空间大小以及时间),根据价值维护一个Region的优先列表,每次都选取列表中第一个进行回收,即回收价值最大的那个Region。

需要考虑的问题是:化整为零后,多个region之间的一些引用如何确定呢(新生代老年代也有该问题),虚拟机采用Remember Set来避免全表扫描。每个region维护一个Remember Set,在对引用类型进行写操作时,会发生写中断,此时检查该引用的对象是否在别的Region中,若是,则将该信息写入他自己所属Region的RememberSet中。以便在对某一个Region进行可达性分析是不需要全表扫描。

STEP

a.初始标记:仅标记GCRoot是直接引用

b.并发标记:并发的标记所有不在引用链上的引用

c.最终标记:在并发标记阶段新产生的对象会写入Remember set log中,在该阶段将Remember Set log中的数据更新进Remember set中,进行标记。

d.筛选回收:!!!不是并发的,对Region的价值进行排序,并根据用户希望的GC停顿时间制定回收计划,由于只回收一个Region,速度也很快,就没有采取并发。
从前面已经知道了,我们根绝对象生存时间的长短不一这一特征,将堆划分为年轻代和年老代。这里进一步了解年轻代内部划分,以及分配对象时区域之间是如何协助的,从而进一步能够知道GC触发的时间。

1)年轻代的划分与基本工作机制

年轻代一般采用复制算法,将年轻代划分为Eden区与FromSurvivor、ToSurvivor。分配对象时,首先分配到Eden与FromSurvivor区中,然后将可达的对象复制到ToSurvivor区中,注意此时将这些对象的年龄+1,并清除Eden+FromS中的无用对象。然后再将FromS变为ToS,ToS变为FromS,(也省去了将To中的对象复制给From中这一步骤)以便下一次的对象回收。

2)对象分配的机制

a. 一般对象首先分配给Eden区,若空间不足则会触发MinorGC。

b. 大对象会直接分配到年老代,若空间不足触发FullGC。

c. 对象在几个情况下将从年轻代进入年老代:

        c1:对象年龄到了(默认为15,在复制过程中年龄增加,因为可达而熬过了一次GC嘛)可通过-XX:MaxTenuringThreshole设置进入年老代的年龄。

        c2:动态对象年龄判断:当年轻代中同一年龄的对象所占存储空间之和大于 Survivor 的一半时(占满了toSurvivor?),将大于等于该年龄的对象都放入年老代

        c3:空间分配担保:年轻代采用复制算法,就需要有空间在survivor区装不下存活对象的时候帮忙存储多出来的对象(一般发生于一次MinorGC之前,存活对象过多导致survivor放不下了),年老代即为空间分配担保的空间。发生空间分配担保时,会进入老年代。

3)空间分配担保机制

年老代进行空间分配担保是有风险的,若担保过来的对象超过了剩余空间,那么年老代会发生FullGC。

所以在发生MinorGC之前,虚拟机会判断新生代所有对象总空间(!!一次保障性的对比,若成立则其子集肯定成立)是否小于年老代最大连续空间大小,若小于,则担保成功,认为这次MinorGC是安全的,执行GC。若大于,则要判断年老代是否开启HandlePromotionFailure(是否允许空间分配担保),若没开启,则直接执行FullGC。若开启了,则判断以往平均担保大小是否小于最大连续空间大小,若小于,则尝试MinorGC(有风险,失败了再FullGC,多一次MinorGC),若大于,则FullGC(包括MinorGC)。

4)总结一下GC触发条件与设置 

MinGC:当Eden区不够时,可设置-XX:SurvivorRatio设置年轻代中Eden的比例,-XX:NewRatio设置堆中年轻代比例,-Xms  -Xmx设置堆最小最大值。

FullGC:a.System.gc()

                b.永久代,类、静态变量,常量太多,不够放  -XX:MaxPermSize

                c.老年代:c1大对象直接进入时不够 c2空间分配担保,MinorGC前,不愿意担保直接FullGC,愿意担保且空间小于之前担保平均值FullGC,若大于但担保失败了(这次太多了)先MinorGC(发现不行)再FullGC。

                d,CMS回收器:只针对老年代收集,浮动垃圾过多,当超过设置的比例大小时(因为用户线程也有空间),会FullGC。设置老年代空间使用比例

-XX:CMSInitialingOccupationFraction,到达这么多时FullGC
目前了解的调优方式:

1)频繁GC时,要使用JDK一些工具排查问题具体位置。

jstat:虚拟机各方面的运行数据  jmap:生成内存转储快照  一般通过这两个就能定位堆中的问题,判断是空间设置不合理还是程序问题。

跟着GC出发点走,调整各个区域大小-Xms -Xmx -XNewRatio -XX:SurvivorRatio  -XX:permSize,以及年老代相关的 maxTenuringTreshold,handlePromotionFailure(一般设为true,以减少FullGC,给MinorGC机会嘛)

2)性能问题,要根据需求选择合适的垃圾回收器,以停顿时间为目标还是以吞吐量为目标

JAVA垃圾回收的工作原理是什么

Java的垃圾回收机制是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间。
需要注意的是:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身,很多人回答的含义是回收对象,实际上这是不正确的。
System.gc()
Runtime.getRuntime().gc()
上面的方法调用时用于显式通知JVM可以进行一次垃圾回收,但真正垃圾回收机制具体在什么时间点开始发生动作这同样是不可预料的,这和抢占式的线程在发生作用时的原理一样。
程序员只能通过上面的方法建议JVM回收垃圾,但是JVM是否回收,同样是不可预料的。
希望能帮到你,望采纳!

如何通知java虚拟机进行垃圾回收以及垃圾回收机制的原理是什么

java的垃圾回收会由虚拟机自动进行。因为各版本虚拟机的实现不一样,具体回收时点会有一定的不同,但大体上在对内存不足时,是一定会尝试进行一次回收的。如果回收后,内存还是不够,则会报出经典的OutofMemory异常。
用户可以调用System.gc()进行强制的内存回收,但和上面一样,回收完后不一定就保证能有足够的内存。
具体原理你可以想象为虚拟机会保存一张森林结构的内存对象表,林中各树的根节点是各个线程,线程中引用的对象,以及这些对象引用的其他对象会按照引用关系依次排列分布到树中。这样当GC进行时,依次扫描所有对象,如果一个对象的父引用指向不到一个处于活动状态的线程,或者所有直接父引用已经标记为可回收,则将这个对象标记为可回收。最后再释放所有标记为可回收的对象内存,达到清理内存垃圾的目的。

java中GC指的是什么

gc是指垃圾回收机制,当一个对象不能再被后续程序所引用到时,这个对象所占用的内存空间就没有存在的意义了,java虚拟机会不定时的去检测内存中这样的对象,然后回收这块内存空间。

GC的基本原理:

  1. 对于程序员来说,用new关键字即在堆中分配了内存,我们称之为“可达”。对于GC来说,只要所有被引用的对象为null时,我们称之为“不可达”,就将进行内存的回收。

  2. 当一个对象被创建时,GC开始监控这个对象的大小、内存地址及使用情况。GC采用有向图的方式记录和管理堆(heap)中的所有对象,通过这种方式可以明确哪些对象是可达的,哪些不是。当确定为不可达时,则对其进行回收。

  3. 保证GC在不同平台的实现问题,java规范对其很多行为没有进行严格的规定。对于采用什么算法,什么时候进行回收等。