Java内存回收机制

欢迎进入Java社区论坛,与200万技术人员互动交流 >>进入

  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软引用

[1][2]

世上没有绝望的处境,只有对处境绝望的人。

Java内存回收机制

相关文章:

你感兴趣的文章:

标签云: