2.Java内存回收机制

一、Java对象在内存引用状态

内存泄露:程序运行过程中,会不断分配内存空间,那些不再使用的内存空间应该即时回收它们,从而保证系统可以再次使用这些内存,如果存在无用的内存没有被回收回来,这就是内存泄漏.
(1)强引用
  这是java程序中最常见的引用方式,程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用.java程序可通过强引用来访问实际的对象。当一个对象被一个或一个以上的强引用变量引用时,它处于可达状态,它不可能被系统垃圾回收机制回收。
  强引用是Java编程中广泛使用的引用类型,被强引用所引用的Java对象绝不会被垃圾回收机制回收,即使系统内存紧张;即使有些Java对象以后永远也不会被用到,JVM也不会回收被强引用所引用的Java对象.
  由于JVM肯定不会回收强引用所引用的JAVA对象,因此强引用是造成JAVA内存泄漏的主要原因。
    如 ReceiptBean rb=new ReceiptBean(); rb就代表了一种强引用的方式
(2)软引用
  软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象;当系统内存空间不足时,系统将回收它.
  软引用通常用在对内存敏感的程序中,软引用是强引用很好的替代。对于软引用,当系统内存空间充足时,软引用与强引用没有太大的区别,当系统内存空间不足时,被软引用所引用的JAVA对象可以被垃圾回收机制回收,从而避免系统内存不足的异常.
  当程序需要大量创建某个类的新对象,而且有可能重新访问已创建老对象时,可以充分使用软引用来解决内存紧张的问题。

例如需要访问1000个Person对象,可以有两种方式
方法一
依次创建1000个对象,但只有一个Person引用指向最后一个Person对象
方法二 定义一个长度为1000个的Person数组,每个数组元素引用一个Person对象.

对于方法一,弱点很明显,程序不允许需要重新访问前面创建的Person对象,即使这个对象所占的空间还没有被回收。但已经失去了这个对象的引用,因此也不得不重新创建一个新的Person对象(重新分配内存),而那个已有的Person对象(完整的,正确的,可用的)则只能等待垃圾回收
对于方法二,优势是可以随时重新访问前面创建的每个Person对象,但弱点也有,如果系统堆内存空间紧张,而1000个Person对象都被强引用引着,垃圾回收机制也不可能回收它们的堆内存空间,系统性能将变成非常差,甚至因此内存不足导致程序中止。

  如果用软引用则是一种较好的方案,当堆内存空间足够时,垃圾回收机制不会回收Person对象,可以随时重新访问一个已有的Person对象,这和普通的强引用没有任何区别。但当heap堆内存空间不足时,系统也可以回收软引用引用的Person对象,从而提高程序运行性能,避免垃圾回收.

当程序使用强引用时,无论系统堆内存如何紧张,JVM垃圾回收机制都不会回收被强引用所引用的Java对象,因此最后导致程序因内存不足而中止。但如果把强引用改为软引用,就完成可以避免这种情况,这就是软引用的优势所在.

(3)弱引用
  弱引用与软引用有点相似,区别在于弱引用所引用对象的生存期更短。弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然,并不是说当一个对象只有弱引用时,它就会立即被回收,正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收.

总结说明:

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

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

(4)虚引用
  软引用和弱引用可以单独使用,但虚引用不能单独使用,单独使用虚引用没有太大的意义。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查虚引用关联的引用队列中是否包含指定的虚引用,从而了解虚引用所引用的对象是否将被回收.
  引用队列由java.lang.ref.ReferenceQueue类表示,它用于保存被回收对象的引用。当把软引用,弱引用和引用队列联合使用时,系统回收被引用的对象之后,将会把被回收对象对应的引用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加到关联的队列中,这使得可以在对象被回收之前采取行动。
虚引用通过PhantomReference类实现,它完全类似于没有引用。虚引用对对象本身没有大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用,那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和队列ReferenceQueue联合使用.

二、Java内存泄露

对于c++程序来说,对象占用的内存空间都必须由程序显式回收,如果程序员忘记了回收它们,那它们所占用的内存空间就会产生内存泄漏;对于java程序来说,所有不可达的对象都由垃圾回收机制负责回收,因此程序员不需要考虑这部分的内存泄漏。但如果程序中有一些java对象,它们处于可达状态,但程序以后永远都不会再访问它们,那它们所占用的空间也不会被回收,它们所占用的空间也会产生内存泄漏.

例如:
有ArrayList的长度是4,有四个元素“网”,“络”,“时”,“空”,当我们删除了ArrayList中的"网"这个元素时,它的size等于3,也就是该ArrayList认为自己只有3个元素,因此它永远也不会去访问底层数组的第4个元素。对于程序本身来说,这个对象已经变成了垃圾,但对于垃圾回收机制来说,这个对象依然处于可达状态,因此不会回收它,这就产生了内存泄漏了  

再看下面程序采用基于数组的方式实现了一个Stack,大家可以找找这个程序中的内存泄漏

 

package list;public class Stack
{//存放栈内元素的数组private Object[] elementData;//记录栈内元素的个数private int size = 0;private int capacityIncrement;//以指定初始化容量创建一个Stackpublic Stack(int initialCapacity){elementData = new Object[initialCapacity];}public Stack(int initialCapacity , int capacityIncrement){this(initialCapacity);this.capacityIncrement = capacityIncrement;}//向“栈”顶压入一个元素public void push(Object object){ensureCapacity();elementData[size++] = object;// if(size==10) System.out.println("size="+size);}//出栈public Object pop(){if(size == 0){throw new RuntimeException("空栈异常");}return elementData[--size];}public int size(){return size;}//保证底层数组能容纳栈内所有元素private void ensureCapacity(){//增加堆栈的容量if(elementData.length==size){Object[] oldElements = elementData;int newLength = 0;//已经设置capacityIncrementif (capacityIncrement > 0){newLength = elementData.length + capacityIncrement;}else{//将长度扩充到原来的1.5倍newLength = (int)(elementData.length * 1.5);}// System.out.println("newLength="+newLength);elementData = new Object[newLength];//将原数组的元素复制到新数组中System.arraycopy(oldElements , 0 , elementData , 0 , size);}}public static void main(String[] args){Stack stack = new Stack(10);//向栈顶压入10个元素for (int i = 0 ; i < 10 ; i++){stack.push("元素" + i);}//依次弹出10个元素for (int i = 0 ; i < 10 ; i++){System.out.println(stack.pop());}}
}

 

     前面程序实现了一个简单的Stack,并为这个Stack实现了push(),pop()两个方法,其中pop()方法可能产生内存泄漏。为了说明这个Stack的内存泄漏,程序main方法创建了一个Stack对象,先向该Stack压入10个元素。注意:此时底层elementData的长度为10,每人数组元素都引用一个字符串。
  接下来,程序10次调用pop()方法弹出栈顶元素。注意pop()方法产生的内存泄漏,它只做了两件事:一是修饰Stack的size属性,也就是记录栈内元素减1,二是返回elementData数组中索引为size-1的元素

  也就是说,每调用pop方法一次,Stack会记录该栈的尺寸减1,但未清除elementData数组的最后一个元素的引用,这样就会产生内存泄漏。类似地,也应该按ArrayList类的源代码改写此处pop()方法的源代码,如下所示
public Object pop()
{
if(size == 0)
{
throw new RuntimeException("空栈异常");
}
Object obj=elementData[--size];
//清除最后一个数组元素的引用,避免内存泄漏
elementData[size]=null;
return obj;
}

三、内存管理的小技巧

  尽可能多的掌握Java的内存回收,垃圾回收机制是为了更好地管理JVM的内存,这样才能提高java程序的运行性能。根据前面介绍的内存机制,下面给出java内存管理的几个小技巧。
(1)尽量使用直接量
  当需要使用字符串,还有Byte,Short,Integer,Long,Float,Double,Boolean,Charater包装类的实例时,程序不应该采用new的方式来创建对象,而应该直接采用直接量来创建它们。
例如,程序需要"hello"字符串,应该采用如下代码
String str="hello"'
上面这种方式创建一个"hello"字符串,而且JVM的字符串缓存池还会缓存这个字符串。但如果程序采用
String str=new String("hello");
  此时程序同样创建了一个缓存在字符串缓存池中的"hello"字符串。除此之外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组里依次存放了h,e,l,l.o等字符串。

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

(3)尽早释放无用对象的引用
  大部分时候,方法局部引用变量所引用的对象会随着方法结束而变成垃圾,因为局部变量的生存周期很短,当方法运行结束之时,该方法内的局部变量就结束了生命周期。因此,大部分时候程序无需将局部引用变量显式设为null.
但是下面程序中的情形则需显式设为null比较好了

public void info()
{
Object obj=new Objec();
System.out.println(obj.toString());
System.out.println(obj.toString());
obj=null;
//执行耗时,耗内存的操作
//或者调用耗时,耗内存的操作的方法
..
}

  对于上面程序所示的info()方法,如果需要“执行耗时,耗内存的操作”或者"或者调用耗时,耗内存的操作的方法",那么上面程序中显式设置obj=null就是有必要的了。可能的情况是:当程序在“执行耗时,耗内存的操作”或者"或者调用耗时,耗内存的操作的方法",obj之前所引用的对象可能被垃圾加收了。

(4)尽量少用静态变量
  从理论来说,Java对象对象何时被回收由垃圾回收机制决定,对程序员来说是不确定的。由于垃圾回收机制判断一个对象是否是垃圾的唯一标准就是该对象是否有引用变量引用它,因此要尽早释放对象的引用。
  最坏的情况是某个对象被static变量所引用,那么垃圾回收机制通常是不会回收这个对象所占用的内存的。

Class Person
{
static Object obj=new Object();
}
  对于上面的Object对象而言,只要obj变量还引用它,就会不会被垃圾回收机制所回收
Person类所对应的Class对象会常驻内存,直到程序结束,因此obj所引用的Object对象一旦被创建,也会常驻内存,直到程序运行结束。

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

public class Test
{
public static void main(String[] args)
{
for(int i=0;i<10;i++)
{
Object obj=new Object();
//执行其它操作...
}
}
}

  上面物循环产生了10个对象,系统要不断地为这些对象分配内存空间,执行初始化操作。它们的生存时间并不长,接下来系统又需要回收它们所占用的内存空间是,这种不断分配内存,回收操作中,程序的性能受到了很大的影响。

(6)缓存经常使用的对象
  如果有些对象需要经常使用,可以考虑把这些对象用缓存池保存起来,这样下次需要时就可直接拿出来这些对象来用。典型的缓存池是数据连接池,数据连接池里缓存了大量的数据库连接,每次程序需要访问数据库时都可直接取出数据库连接。
  除此之外,如果系统里还有一些常用的基础信息,比如信息化信息里包含的员工信息,物料信息等,也可以考虑对它们进行缓存。
使用缓存通常有两种方法
1.使用HashMap进行缓存
2.直接使用开源缓存项目。(如OSCache,Ehcahe等)

(7)尽量不要用finalize方法
  在一个对象失去引用之后,垃圾回收器准备回收该对象之前,垃圾回收器会先调用对象的finalize()方法来执行资源清理。出于这种考虑,可能有些开发者会考虑使用finalize()方法来进和清理。
在垃圾回收器本身已经严重制约应用程序性能的情况下,如果再选择使用finalize方法进行资源清理,无疑是一种火上浇油的行为,这将导致垃圾回收器的负担更大,导致程序运行效率更低

(8)考虑使用SoftReference软引用
   

转载于:https://www.cnblogs.com/jiutianhe/archive/2012/10/07/2755670.html

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

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

相关文章

C++ 对象的初始化和清理

4.2对象的初始化和清理 ●生活中我们买的电子产品都基本会有出厂设置,在某-天我们不用时候也会删除一些自己信息数据保证安全 ●C中的面向对象来源于生活&#xff0c;每个对象也都会有初始设置以及对象销毁前的清理数据的设置。 4.2.1构造函数和析构函数 对象的初始化和清理也是…

VMware-workstation-full-8.0.0-471780.exe

下载地址&#xff1a;http://115.com/file/bhzruvd6完美汉化补丁&#xff1a;http://115.com/file/clq88zcuVMware Workstation 8.0_HA_V3 .exe文件大小&#xff1a;80466532 字节文件版本&#xff1a;8.0.0.471780修改时间&#xff1a;2011年9月24日 10:45:09MD5 &#xff1a;…

查找当前地形位置上的贴图信息

http://answers.unity3d.com/questions/34328/terrain-with-multiple-splat-textures-how-can-i-det.html转载于:https://www.cnblogs.com/klobohyz/archive/2012/10/09/2716627.html

C++ 深拷贝与浅拷贝

#include<iostream> using namespace std; #include<string>class Person {public:Person() {cout << "Person的无参构造函数调用" << endl;};Person(int age) {m_Age age;cout << "Person的有参构造函数调用" << en…

转载]Cyclone II JTAG ASP 配置下载程序

原文&#xff1a;http://blog.sina.com.cn/s/blog_4739958a0100irp7.html 首先&#xff0c;还是那句话&#xff0c;电脑上写好程序.pof文件直接通过JTAG写到FPGA SRAM里&#xff0c;掉电丢失。 只有把.pof写到串行配置器件上&#xff0c;板子上电后串行配置器件EPCS4将程序自动…

C++ 类和对象成员特性

//当类中成员是其他类对象时&#xff0c;我们称该成员为对象成员 //构造的顺序是:先调用对象成员的构造&#xff0c;再调用本类构造 //析构顺序与构造相反 #include <iostream> using namespace std; #include <string>class Phone{public:Phone(string name){m_P…

android surfaceview 清屏

引用&#xff1a;http://www.eoeandroid.com/forum.php?modviewthread&tid40776 12楼 清屏可以用橡皮擦的画笔画一个屏幕大小的矩形就可以了//这是定义橡皮擦画笔Paint clearPaint new Paint();clearPaint.setAntiAlias(true);clearPaint.setXfermode(new PorterDuffX…

C++ 静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员 静态成员分为: ●静态成员变量 。所有对象共享同一份数据 。在编译阶段分配内存 。类内声明&#xff0c;类外初始化 ●静态成员函数I 。所有对象共享同一个函数 。静态成员函数只能访问静态成员变量 #include…

ScaleGestureDetector使用注意事项

注&#xff1a;一定要通过view的onTouchEvent调用mScaleGestureDetector.onTouchEvent(ev);&#xff0c;只有这样 才能调用回调函数&#xff1a;onScaleBegin 具体可以参加&#xff1a;android源码&#xff1a;KenBurnsActivity.java 下面是转载的文章&#xff1a; Detects tra…

C++ 静态成员函数

#include <iostream> using namespace std; #include <string>class Person{public://静态成员函数特点://1程序共享一个函数//2静态成员函数只能访问静态成员变量static void func(){cout << "func调用" << endl;m_A 100;//m. _B 100; //…

提高编写CSS代码效率的10个习惯

提高编写CSS代码效率的10个习惯1、保持一贯性。 就像其它的任何事一样&#xff0c;值得一直保持一贯性。保持连贯性&#xff0c;而不是想到什么就给id和class命名什么。 CSS的级联样式有利于加深你的记忆&#xff0c;而且充分利用样式的继承去设置样式表。 首先声明通用的部分的…

hdu 1257 最少拦截系统 (DP)

点击打开链接 #include"stdio.h" int main() {int a[10011],b[10011];int i,j,c,n;while(scanf("%d",&n)!EOF){c1;for(i0;i<n;i)scanf("%d",&a[i]);b[0]a[0];for(i1;i<n;i){for(j0;j<c;j){if(a[i]<b[j])break;}b[j]a[i];if(…

C++ 友元函数

生活中你的家有客厅(Public),有你的卧室(Private) 客厅所有来的客人都可以进去&#xff0c;但是你的卧室是私有的&#xff0c;也就是说只有你能进去 但是呢&#xff0c;你也可以允许你的好闺蜜好基友进去。 在程序里&#xff0c;有些私有属性也想让类外特殊的一些函数或者类进行…

九度1377缓变序列

#include<stdio.h> #include<string.h> int n,m,k,i,j,minN,a[10003]; main() {while(~scanf("%d",&n)){memset(a,0,sizeof(a));minN0x7ffffff,m0,j0;for(i0;i<n;i)scanf("%d",&k),minNminN<k?minN:k,a[k]0?m:0,a[k];if(m1)pr…

关于计算机类课程实验教学的思考

由于计算机机房的限制&#xff0c;没有安装投影仪以及计算机控制管理软件&#xff0c;所以单纯的讲解效果非常不好。老师在上面讲&#xff0c;学生在下面干什么根本不知道。由于教授的实验课和理论课关系不是非常密切。试验的主要内容是网页设计&#xff0c;也就是如何利用Drea…

第四章类和对象 习题答案

一&#xff0e;选择题1.能提供封装的C的下列关键字是&#xff08;C&#xff09;A.whileB. unionC. classD. for2.在下面所列项中&#xff0c;不是面向对象的特点的是&#xff08;C&#xff09;A. 多面性B. 抽象性和封装性C. 多线程和多任务D. 继承性3.下面选项中&#xff0c;对…

在Javascript中闭包(Closure)

一、什么是闭包&#xff1f; “官方”的解释是&#xff1a;所谓“闭包”&#xff0c;指的是一个拥有许多变量和绑定了这些变量的环境的表达式&#xff08;通常是一个函数&#xff09;&#xff0c;因而这些变量也是该表达式的一部分。相信很少有人能直接看懂这句话&#xff0c;因…

C++ 文件写操作

#include <fstream> #include<iostream> using namespace std;void test01(){ofstream ofs;ofs.open("test.txt",ios :: out);ofs << "姓名:张三"<< endl;ofs << "性别:男"<< endl;ofs << "年龄…

JS如何获取URL

JS如何获取URL 一、var urldocument.location; url "www.abc.com/sports/def.aspx"; var r /\/(\w)\.aspx/; alert(url.match(r)[1]); alert(r.exec(url)[1]);二、 var url document.locationurl " http://localhost:8080/Customer/index?tokenghfghfghfghfg…

winform 分页控件分享(二)

大数量分页&#xff0c;使用存储过程。 这个存储过程是网络上考的&#xff0c;呵呵。我把它给贴出来&#xff0c;希望原作者别砸我砖头。。。。。 ALTER PROCEDURE SP_Pagination/**//****************************************************************** 千万数量级分页存…