1.Java的内存回收

大致内容,如下所示:
》JVM在何时决定回收一个Java对象所占用的内存?
》JVM会不会漏掉回收某些Java对象,使之造成内存泄漏?
》JVM回收 lava对象所占用内存的实现细节。
》JVM能否对不同的Java对象占用的内存区分对待、回收?
》常见垃圾回收机制的实现细节是怎样的?

**********************************************************************************************

对象在内存中的状态

对于jvm的回收机制来说,是否回收一个对象的标准在于:是否还有引用变量引用该对象?只要有引用变量引用该对象,垃圾回收机制就不会回收它

基本上,可以把JVM内存中的对象引用理解为一种有向图,把引用变量,对象都当作有向图的顶点,将引用关系当作图的有向边,有向边总是从引用端指向被引用对象。因为Java的所有对象都是有一条条线程创建出来的,因此可以把线程对象当成有向图的顶点。

只要从有向图的起始顶点(也就是进程根)不可到达它们,垃圾回收机制就会回收它们。采用有向图来管理内存中的对象具有较高的精度,但缺点是效率较低。


当一个对象在堆内存中运行时,根据它在对应有向图中的状态,可以把它所处的状态分成如下三种:
》可达状态;:当一个对象被创建后,有一个以上的引用变量引用它。在有向图中可以从起始顶点导航到该对象,那么它就处于可达状态,程序可以通过引用变量来调用该对象的属性和方法。
》可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它将先进入可恢复状态,系统调用fimalize方法可以重新让其变为可达状态

》不可达状态:当对象的所有关联都被切断,且系统调用所有对象的fimalize方法依然没有恢复可达状态

一个对象可以被一个方法的局部变量引用,也可以被其他类的类变量引用,或者被其他对象的实例变量引用。当某个对象被其他类的类变量引用时,只有该类被销毁后,该对象才会进入可恢复状态;当某个对象被其他对象的实例变量引用时,只有当引用该对象的对象被销毁或变成不可达状态后,该对象才会进入不可达状态。

引用分类的由来

对垃圾回收机制来说,判断一个对象是否可回收的标准就在于该对象是否被引用,因此引用也是JVM进行内存管理的一个重要概念。为了更好地管理对象的引用,从JDK1.2开始,Java在java.lang.ref包下提供了三个类;SoftReference、PhantomReference和WeakReference。

它们分别代表了系统对对象的三种引用方式:软引用、虚引用和弱引用。归纳起来,Java语言对对象的引用有如下四种:


》强引用

这是Java程序中最常见的引用方式,程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用。

由于JVM肯定不会回收被强引用所引用的Java对象,因此强引用是造成Java内存泄漏的主要原因之一。



》软引用

软引用通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收。

当系统内存充足,则不会被回收,如果内存紧张,被软引用的对象则可以被回收,提高程序运行性能。

因此,SoftReference 引用对象非常适合实现内存敏感的缓存,例如加载图片的时候,bitmap缓存机制。

用法举例:

一,

String value = new String(“sy”);
SoftReference sfRefer = new SoftReference (value );
sfRefer .get();//可以获得引用对象值

二,

SoftReference<Person>[]people = new SoftReerence[10000];
for (int i=0;i<people.length;i++)
{
   people[i]=new SoftReference<Person>(new Person("age","name"))
}

》弱引用

具有弱引用的对象,与软引用对比来说,前者的生命周期更短。当垃圾回收器扫描到弱引用的对象的时候,不管内存空间是否足够,都会直接被垃圾回收器回收。不过也不用特别担心,垃圾回收器是一个优先级比较低的现场,因此不一定很快可以发现弱引用的对象。

用法实例:

String value = new String(“sy”);
WeakReference weakRefer = new WeakReference(value );

System.gc();
weakRefer.get();//null

弱引用具有很大的不确定性,因为每次垃圾回收机制执行时都会回收弱引用所引用的对象,而垃圾回收机制的运行又不受程序员的控制,因此程序获取弱引用所引用的Java对象时必须小心空指针异常—通过弱引用所获取的Java对象可能是null。


由于垃圾回收的不确定性,当程序希望从弱引用中取出被引用对象时,可能这个被引用对象已经被释放了。如果程序需要使用那个被引用的对象,则必须重新创建该对象。

》虚引用

 虚引用通过PhantomReference类实现,它完全类似于没有引用。虚引用对对象本身没太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用,那么 它和没有用的效果大致相同
虚引用主要用于 跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚用必须和引用队列(ReferenceQueue)联合使用。 


小节拾遗:

在强引用中,如果不让该对象指向为空,垃圾回收器绝对不会回收它。除非当出现内存空间不足的时候。jvm抛出oom导致程序异常种植的时候,才会回收具有强引用的对象来解决内存空间不足问题。

   Object obj =new Object();  // 强引用
   obj = null;//这时候为垃圾回收器回收这个对象,至于什么时候回收,取决于垃圾回收器的算法
**************************************************************************************************************


Java的内存泄漏

程序运行过程中会不断地分配内存空间,那些不再使用的内存空间应该即时被回收,从而保证系统可以再次使用这些内存, 如果存在无用的内存没有被回收回来,那就是内存泄漏。
相比较后,C++的内存泄漏比Java的更危险,因为它的无用对对象将永远不可达。


应用:

ArrayList的整体搬移

Stack的pop()方法

**ElementData[size]==null;


*************************************************************************************************************


垃圾回收机制


垃圾回收机制主要完成以下两种事情:

1. 跟踪并监控每一个Java对象,当某个对象处于不可达状态时,回收该对象所占用的内存。

2. 清理内存分配,回收过程中产生的内存碎片


垃圾回收的基本算法

对于一个垃圾回收器的设计算法来说,大致有如下可供选择的设计:

串行回收和并行回收:

并发执行和应用程序停止:

 

垃圾回收器回收已用内存空间的方式

压缩(标记压缩)

不压缩(标记清除)

复制

 

堆内存的分代回收

现行的垃圾回收器用分代的方式采用不同的回收算法

Young

Old

Permanent

 

分代回收的一个依据就是对象生存时间的长短,然后根据不同代采取不同的垃圾回收策略。

采用这种“分代回收”的策略基于如下两点事实。

1)绝大对数的对象不会被长时间引用,这些对象在其Young期间就会被回收。

2》很老的对象(生存时间很长)和很新的对象(生存时间很短)之间很少存在相互引用的情况。

 

Young


根据上面两点事实,对于Young代的对象而言,大部分对象都会很快就进入不可达状态,

只有少量的对象能熬到垃圾回收执行时,而垃圾回收器只需要保留Young代中处于可达状态的对象,如果采用复制算法只需要少量的复制成本,因此大部分垃圾回收器对Young代都采用复制算法。

 

OLD

如果Young代中的对象经过数次垃圾回收依然没有被回收掉,即这个对象经过足够长的时间还处于可达状态,垃圾回收机制就会将这个对象转移到Old代。

Old代回收的特点:

1.Old代垃圾回收的执行频率无须太高,因为很少有对象会死掉。

2.每次对Old代执行垃圾回收都需要更长的时间来完成。

 

Permanent

Permanent代主要用于装载Class、方法等信息,默认为64MB,垃圾回收机制通常不会回收Permanent代中的对象。对于那些需要加载很多类的服务器程序,往往需要加大Permanent代的内存,否则可能会因为内存不足而导致程序终止。

 

总结:

Young代的内存将要用完时,垃圾回收机制会对Young代进行垃圾回收,垃圾回收机制会采用较高的频率对Young代进行扫描和回收。因为这种回收的系统开销比较小,因此也被称为次要回收(minor collection)

Old代的内存将要用完时,垃圾回收机制会进行全回收,也就是对Young代和Old代都要进行回收,此时回收成本就大得多了,因此也称为主要回收(major collection)

 

通常来说,Young代的内存会先被回收,而且会使用专门的回收算法(复制算法)来回收Young代的内存;对于Old代的回收频率则要低得多,因此也会采用专门的回收算法。

如果需要进行内存压缩,那么每个代都独立地进行压缩。

 


常见的垃圾回收器

1. 串行回收器

2. 并行回收器

3. 并行压缩回收器

4. 并发标识-清理回收器(不压缩)

 ********************************************************************************************


内存管理小技巧

 

1. 尽量使用直接量

当需要使用字符串,还有ByteShortIntegerLongFloatDoubleBooleanCharacter包装类的实例时,程序不应该采用new的方式来创建对象,而应该直接采用直接量来创建它们。

 

2使用StringBuilder StringBufer进行字符串连接

StringStringBuilderStringBuffer都可代表字符串,其中String代表字符序列不可变的字符串,而StringBuilder StringBuffer都代表字符序列可变的字符串。

如果程序使用多个String对象进行字符串连接运算,在运行时将生成大量的临时字符串,这些字符串会保存在内存中从而导致程序性能下降。

 

3.尽早释放无用对象的引用

 分两种情况:

方法的局部变量所引用的对象会随着方法的结束而变成垃圾,此时无需用null将其显式释放

书上的例子:

对于上面程序所示的info0方法,如果在粗体字代码后还需要执行耗时、耗内存操作,或者还需要调用耗时、耗内存的方法,那么程序中的粗体字代码就是有必要的:

可以尽早释放对Objiect对象的引用。可能的情况是,程序在执行粗体字代码之后的耗时、耗内存操作时,obj之前所引用的Object对象可能被垃圾回收了。

 

4尽量少使用静态变量

 

5避免在经常调用的方法,循环中创建JAVA对象

 

6记得缓存经常使用的对象

两种方法:使用hashmap进行缓存

直接使用某些开源的缓存项目

 

7尽量不要使用finalize方法

 

8考虑使用弱引用特别是创建数量很大的数组时


********************************************************************************************

 参考链接

Java中内存优化的SoftReference 和 WeakReference浅析 - 简书  https://www.jianshu.com/p/b56731447179


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值