java垃圾回收机制

为什么80%的码农都做不了架构师?>>>   hot3.png

Java垃圾回收机制

        C++程序员觉得内存管理太重要了,所以一定需要自己管理,

                  java程序员觉得内存管理太重要了,所以一定不能自己管理!


一、简述

        垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用,以免造成内存泄露。

        : 垃圾回收能自动释放内存空间,减轻编程的负担。这使Java 虚拟机具有一些优点。首先,它能使编程效率提高。在没有垃圾回收机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候,靠垃圾回收机制可大大缩短时间。其次是它保护程序的完整性, 垃圾回收是Java语言安全性策略的一个重要部份。

        :有得必有失,GC一定程度上影响程序性能。因为Java虚拟机必须追踪运行程序中有用的对象,而且最终释放没用的对象。这一个过程需要花费处理器的时间。其次垃圾回收算法的不完备性,早先采用的某些垃圾回收算法就不能保证100%收集到所有的废弃内存。当然随着垃圾回收算法的不断改进以及软硬件运行效率的不断提升,这些问题都可以迎刃而解。

二、JVM何时进行GC      

        一般是在CPU空闲或空间不足时自动进行垃圾回收,而程序员无法精确控制垃圾回收的时机和顺序等。

        JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:

  1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

2) Java堆内存不足时,GC会被调用当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

    由于是否进行主GCJVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的


三、垃圾回收算法

最常见的回收:

1 空引用 :当对象没有对他可到达引用时,他就符合垃圾回收的条件。也就是说如果没有对他的引用,删除对象的引用就可以达到目的,因此我们可以把引用变量设置为 null,来符合垃圾回收的条件。
 

StringBuffer sb = new StringBuffer("hello");   System.out.println(sb);   sb=null;


2 重新为引用变量赋值:可以通过设置引用变量引用另一个对象来解除该引用变量与一个对象间的引用关系。

StringBuffer sb1 = new StringBuffer("hello");StringBuffer sb2 = new StringBuffer("goodbye");System.out.println(sb1);sb1=sb2;//
此时
"hello"
符合回收条件


(3) 方法内创建的对象:所创建的局部变量仅在该方法的作用期间内存在。一旦该方法返回,在这个方法内创建的对象就符合垃圾收集条件。有一种明显的例外情况,就是方法的返回对象。

public static void main(String[] args) {Date d = getDate();System.out.println("d = " + d);
}
private static Date getDate() {Date d2 = new Date();StringBuffer now = new StringBuffer(d2.toString());System.out.println(now);return d2;
}

(4) 隔离引用:这种情况中,被回收的对象仍具有引用,这种情况称作隔离岛。若存在这两个实例,他们互相引用,并且这两个对象的所有其他引用都删除,其他任何线程无法访问这两个对象中的任意一个。也可以符合垃圾回收条件。

public class Island {Island i;public static void main(String[] args) {Island i2 = new Island();Island i3 = new Island();Island i4 = new Island();i2.i=i3;i3.i=i4;i4.i=i2;i2=null;i3=null;i4=null;}
}

 下面简单说一下JVM是如何知道哪些对象需要被回收?

            引用计数法  

        每个对象上都有一个引用计数,对象每被引用一次,引用计数器就+1,对象引用被释放,引用计数器-1,直到对象的引用计数为0,对象就标识可以回收,这个可以用数据算法中的图形表示,对象A-对象B-对象C 都有引用,所以不会被回收,对象B由于没有被引用,没有路径可以达到对象B,对象B的引用计数就就是0,对象B就会被回收。

170406_q9EF_2295363.png

但是这个算法有明显的缺陷,对于循环引用的情况下,循环引用的对象就不会被回收。例如下图:对象A,对象B 循环引用,没有其他的对象引用AB,则AB 都不会被回收。

    170448_SESV_2295363.png


    root搜索算

        这种算法目前定义了几个root,也就是这几个对象是jvm虚拟机不会被回收的对象,所以这些对象引用的对象都是在使用中的对象,这些对象未使用的对象就是即将要被回收的对象。简单就是说:如果对象能够达到root,就不会被回收,如果对象不能够达到root,就会被回收。

    170538_2MaZ_2295363.png


由于这种算法即使存在互相引用的对象,但如果这两个对象无法访问到根对象,还是会被回收。如下图:对象C和对象D互相引用,但是由于无法访问根,所以会被回收。

170550_jqZ1_2295363.png


        jvm在确定是否回收的对象的时候采用的是root搜索算法来实现。

    在root搜索算法的里面,我们说的引用这里都指定的是强引用关系。

        所谓强引用关系,就是通过用new 方式创建的对象,并且显示关联的对象.

        下面对引用关系进行一些简单的补充:

Object obj = new  Object();

    以上就是代表的是强引用关系,变量obj 强引用了 Object的一个对象。

    java里面有四种应用关系,从强到弱分别为:

        Strong Reference(强引用) –>Weak Reference (弱引用) -> Soft Reference(软引用) – > Phantom Reference(引用)


        Strong Reference 只有在引用对象root不可达的情况下才会标识为可回收,垃圾回收才可能进行回收

        Weak Reference :即使在root算法中 其引用的对象root可达到,但是如果jvm堆内存 不够的时候,还是会被回收。

        Soft Reference 无论其引用的对象是否root可达,在响应内存需要时,由垃圾回收判断是否需要回收。

        Phantom Reference :在回收器确定其指示对象可另外回收之后,被加入垃圾回收队列.

 

四、如何减少GC的开销

        (1)不要显式调用System.gc()

  此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

         调用System.gc()只是建议JVM进行GC。至于JVM到底会不会做,那就不好说啦。通常不建议自己手动调用System.gc(),还是让JVM自行决定比较好。另外,使用JVM命令行参数“-XX:+DisableExplicitGC”可以让System.gc()不起作用。

       (2) 避免隐式的String字符串

       String字符串是我们管理的每一个数据结构中不可分割的一部分。它们在被分配好了之后不可以被修改。比如"+"操作就会分配一个链接两个字符串的新的字符串。更糟糕的是,这里分配了一个隐式的StringBuilder对象来链接两个String字符串.

       尽量使用StringBuffer,而不用String来累加字符串

       (3) 计划好List的容量

       ArrayList这样的动态集合用来存储一些长度可变化数据的基本结构。ArrayList和一些其他的集合(如HashMapTreeMap),底层都是通过使用Object[]数组来实现的。而String数组(它们自己包装在char[]数组中)大小是不变的。那么问题就出现了,如果它们的大小是不变的,我们怎么能放item记录到集合中去呢?答案显而易见:通过动态分配数组。

    看下面的例子:

 List<Item> items = new ArrayList<Item>();for (int i = 0; i < len; i++)
{
Item item = readNextItem();
items.add(item);
}

        len的值决定了循环结束时items 最终的大小。然而,最初,ArrayList的构造器并不知道这个值的大小,构造器会分配一个默认的Object数组的大小。一旦内部数组溢出,它就会被一个新的、并且足够大的数组代替,这就使之前分配的数组成为了垃圾。

    如果执行数千次的循环,那么就会进行更多次数的新数组分配操作,以及更多次数的旧数组回收操作。对于在大规模环境下运行的代码,这些分配和释放的操作应该尽可能从CPU周期中剔除。

    解决方案:

    无论什么时候,尽可能的给List或者Map分配一个初始容量,就像这样:

List<MyObject> items = new ArrayList<MyObject>(len);

    因为List初始化,有足够的容量,所有这样可以减少内部数组在运行时不必要的分配和释放。如果你不知道确定的大小,最好估算一下这个值的平均值,添加一些缓冲,防止意外溢出。

(4)对象不用时最好显式置为Null

  一般而言,Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

(6)尽量少用静态对象变量

  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

等等,其他还有很多关于gc调优的方法,希望大家自行摸索,并能够学以致用.

五、补充

       finalize()方法) java提供了一种机制,使你能够在对象刚要被垃圾回收之前运行一些代码。这段代码位于名为finalize()的方法内,所有类从Object类继承这个方法。由于不能保证垃圾回收器会删除某个对象。因此放在finalize()中的代码无法保证运行。因此建议不要重写finalize();

       finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.

         finalize()在什么时候被调用?
        
有三种情况
         1.
所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候.
         2.
程序退出时为每个对象调用一次finalize方法。
         3.
显式的调用finalize方法(不建议)

下面是javaDoc中对finalize()方法的解释:

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。

finalize 的常规协定是:当 JavaTM虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法,除非由于准备终止的其他某个对象或类的终结操作执行了某个操作。finalize 方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。例如,表示输入/输出连接的对象的 finalize 方法可执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。

Object 类的 finalize 方法执行非特殊性操作;它仅执行一些常规返回。Object 的子类可以重写此定义。

Java 编程语言不保证哪个线程将调用某个给定对象的 finalize 方法。但可以保证在调用 finalize 时,调用 finalize 的线程将不会持有任何用户可见的同步锁定。如果 finalize 方法抛出未捕获的异常,那么该异常将被忽略,并且该对象的终结操作将终止。

在启用某个对象的 finalize 方法后,将不会执行进一步操作,直到 Java 虚拟机再次确定尚未终止的任何线程无法再通过任何方法访问此对象,其中包括由准备终止的其他对象或类执行的可能操作,在执行该操作时,对象可能被丢弃。

对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。

 

转载于:https://my.oschina.net/dadou/blog/481388

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

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

相关文章

C语言顺序结构程序设计PPT,C语言21(顺序结构程序设计之1).ppt

《C语言21(顺序结构程序设计之1).ppt》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《C语言21(顺序结构程序设计之1).ppt(28页珍藏版)》请在人人文库网上搜索。1、上一节我们学了,C语言程序的结构 C语言程序的书写规范,第2章 最简单的C程序设计,-顺序程序设计,C程序常…

神PS!老爸把儿子的画作P成现实,看完我笑哭了

全世界只有3.14 % 的人关注了爆炸吧知识只要你敢画&#xff0c;我就敢P成现实这种脑洞大开的事发生在英国的一对父子身上儿子天马行空地乱画爸爸认认真真地P出来象君几年前在ins发现了这对宝藏父子没想到他们的疯狂依旧在继续着四脚怪物系列看上去好像也没什么毛病你就是我见过…

单体应用 适合采用 dapr 构建吗?

缘起今天在微信群里有同学问 ”纯.net 项目&#xff0c;有必要上dapr吗&#xff1f;” 当时不假思索的说不是微服务没必要&#xff0c;其他群友也说没必要。下午细想了一下&#xff0c;觉得这个和微服务没有关系&#xff0c;如果我的应用是个单体架构&#xff08;将所有功能都部…

在主窗体中打开一个新子窗体,如果已有子窗体,则激活它,而不打开新的。...

frmGroupMgr fgm null; //遍历窗体中是否已存在同名的子窗体 foreach (Form f in this.MdiChildren) { //检测是不是当前子窗体名称 if (f.Text "组管理") { f.Activate(); fgm f as frmGroupMgr; break; } } //窗体中没有该子窗体 则打开一个新窗体 if (fgm nul…

EqualLogic全攻略视频[(四)高级管理]

制片人&#xff1a; 戴尔中国大客户部高级市场经理 Andy Peng 彭宇恒 演讲者&#xff1a; 戴尔亚太区存储技术总监 Alvin Kho 许良谋 戴尔中国高级系统工程师 English Li 李英文 EqualLogic全攻略视频[&#xff08;四&#xff09;高级管理] 更多精彩文章请关注: 戴尔技术社区转…

一次面试引发的思考(中小型网站优化思考) (转)

前言 故事的起因是这样的&#xff0c;由于本人地处偏僻工作地点在美丽的冰城哈尔滨虽然地方很美丽&#xff0c;但是这里的软件行业实在是算不上“美丽”&#xff0c;这么多年由于个人原因或者公司原因经常换工作&#xff0c;因为这里都是中小型公司&#xff0c;没有什么大公司。…

string 字符串中字符无效_JavaScript中的字符串(string)到底是什么?

通过可见字符建模把JavaScript中的string当作字符序列来看待是最直观的&#xff0c;虽然这样并不准确。以下代码示例中的字符串由5个字母和一个感叹号组成&#xff1a;const message Hello!;如果把string当作是可见的字符序列&#xff0c;那么Hello!中的字符数是6&#xff1a;…

c语言二维数组代码,C语言之二维数组(示例代码)

二维数组及其指针1 ) 在了解二维数组之前&#xff0c;我们先来了解一维数组及其指针我们知道&#xff0c;一维数组中&#xff1a;数组名代表-->数组首元素的首地址(千万不要认为是数组的首地址(&a)&#xff0c;绝对不是)在内存中&#xff0c;该代码的表现形式如下图&…

颜宁分享干货:给实验室博士的一些忠告

全世界只有3.14 % 的人关注了爆炸吧知识本文来源&#xff1a;颜宁微博nyouyou&#xff0c;作者&#xff1a;颜宁前几日&#xff0c;颜宁在微博上与大家分享自己做实验记录的心得。在此贴出&#xff0c;与大家分享~从我进Shi Lab的第一天&#xff0c;就被导师灌输&#xff1a;好…

研发考核难的本质是因为这三个特点

大家好&#xff0c;我是Z哥。我坦白&#xff0c;这篇是早就写好的库存文章&#xff0c;包括上周的那篇也是。原因是最近跳槽了&#xff0c;到新公司忙得飞起&#xff0c;都没时间写文章。还好我之前未雨绸缪准备了几篇提前写好的文章作为余量&#xff5e;我尽量能保持不断更&am…

点击User Profile Service Application 报错

给客户部署的MOSS2010 SP1版本&#xff0c;部署完毕后&#xff0c;发现点击User Profile Service Application服务进行配置的时候&#xff0c;总是报错&#xff0c;上网查了一下&#xff0c;发现原来是微软的一个补丁包没有安装&#xff0c;于是下载了这个几乎1G的补丁包&#…

剑指offer-面试题13.在O(1)时间删除链表节点

题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。 链表节点与函数的定义如下。 通常我们删除某个节点都是从头开始遍历到需要删除节点的前一个节点。 然后使得该节点的next指向删除节点的next即可&#xff0c;这样看来删除一个节点 的复杂度为O(n)…

c语言程序stm8s,stm8s的c语言编程例程

stm8s的c语言编程例程实例一&#xff1a;控制灯的亮灭(或者蜂鸣器响&#xff0c;只要连接相应端口就可以了)&#xff1a;#i nclude "stm8s.h"〃头文件#defi ne ulong un sig ned long///void delay( ulong i){ulo ng j;for(j0;j{&#xff1b;}} 延时函数void mai n( …

python画图比赛_Python选修课第二届Turtle绘图大赛

20181101173,荀梓恒20181101197,叶普旭代码如下&#xff1a;import turtle as tt.setup(width0.5, height0.75, startxNone, startyNone)t.hideturtle()t.speed(10)t.pensize(2)t.penup()t.goto(100,200)t.pendown()t.setheading(90)t.begin_fill()t.colormode(255)t.fillcolor…

为什么不能一次走遍哥尼斯堡的7座桥

全世界只有3.14 % 的人关注了爆炸吧知识数学的快乐到底有多简单今天&#xff0c;8岁表妹问了一个问题&#xff1a;看到这种类似11&#xff1f;的问题&#xff0c;超模君几乎不用思考就已经知道答案。但为了体现让表妹系统的理解知识&#xff0c;所以我决定......发生在哥尼斯堡…

官宣,11月8号,.NET6+64位VS璀璨面世!

12号&#xff0c;.NET6 RC2发布&#xff0c;13号&#xff0c;VS2022 RC版发布&#xff0c;11月8号&#xff0c;.NET6VS2022C#10 正式版将同时发布&#xff0c;.NET开发将步入全新篇章&#xff0c;各种新语法、新框架、新技术都如约而至&#xff0c;令人期待&#xff01;然而&am…

IUnknown接口QueryInterface函数介绍

一、COM组件的目标&#xff1a; COM组件的一个主要优势是&#xff1a;便于升级。 要实现这个优势需要满足一下两个条件&#xff1a; 1、运行时从客户程序动态加载和卸载&#xff0c;采用DLL技术可以实现。 2、为了更好的突出DLL的优势&#xff0c;还需要信息隐藏&#xff0c;即…

stc单片机c语言 pdf,STC单片机C语言程序设计 第13章 STC单片机C语言指针.pdf

STC单片机C语言指针主讲&#xff1a;何宾Email &#xff1a;hebinmail.buct.edu.cn2016.03C51编译器中指针的分类Cx51编译器支持使用字符“*”声明变量指针。C51编译器可以执行在标准C中所提供的所有操作。但是&#xff0c;由于8051独一无二的结构以及特点&#xff0c;C51编译器…

小猿学python_小猿圈python入门之转行零基础该如何学Python?

转行零基础学Python编程开发难度大吗&#xff1f;从哪学起&#xff1f;近期很多小伙伴问我&#xff0c;如果自己转行学习Python&#xff0c;完全0基础能否学会呢&#xff1f;Python的难度到底有多大&#xff1f;今天&#xff0c;小编就来为大家解决一下疑惑。学习Python难吗&am…

dojo Quick Start/dojo入门手册--json

那要是我想更换获取到的数据类型&#xff0c;比如json&#xff1f;xml&#xff1f;修改handleAs即可&#xff0c;如&#xff1a; handleAs: "json" dojo.xhrGet({ url: "http://localhost/hello/sayHello.jsp", handleAs: "json", load…