JVM内存错误问题的处理建议

Unloading class sun.reflect.GeneratedMethodAccessor

问题可能的原因一:由于Xms和Xmx设置大小不当造成的,当程序进行反射操作时没有内存进行此操作。

    解决方案:使用-XX:PermSize 与 -XX:MaxPermSize  来增加Perm空间大小。因为通过reflect调用方法,实际上,都会生成一个实际的class,并且在perm空间装载。而如果perm空间不够大,同时GC又无法卸载原来装载的类,则会抛出异常。

     1.设置MaxPermSize

MaxPermSize可以设置为最大堆分配(mx)的一半。尝试设置.XX:MaxPermSize=256m,但是,也有人设置到和MX一样的.可以尝试一下.

2.HeapSize

你的ERROR不是OUTOFMEMEROY,所以HEAP的大小应该可用.-Xms512m-Xmx512m可以试一试.但是最近4月底有人发表意见说设成相同的比太好,会导致GC运行时间过短,导致效率下降.

3.HeapFreeRatio

这项设置对你的问题有没有帮助不太清楚,做一个比较反差的测试,-XX:MinHeapFreeRatio=10,-XX:MaxHeapFreeRatio=20,看看区别,建议最终设置:

-XX:MinHeapFreeRatio=30,-XX:MaxHeapFreeRatio=70

4.NewRatio

有可能是younggeneration的大小过大,导致permanentgeneration大小不够,所以调整NewRatio有可能有用.

           -XX:NewRatio=4

    结论:尝试将Xms设小了一些,问题仍未解决,只是出现错误的周期变长。 如果初始值(Xms)太大,虽然开始阶段不会有多少GC,但是,由于一开始就在一个很大的空间分配,所以,会出现很多碎片,(这好像叫暗碎片dark heap),就会浪费很多heap,(我好像没有见过SUN的JVM有compaction,即碎片整理)。如果heap的初始值比较小,那么就能减少很多碎片的产生,它能使heap被占满的时间周期变长。

问题可能的原因二:代码中肯定有内存泄露。GC很可能没有及时的回收内存垃圾,所以造成了momery leak。

    建议:不能依赖GC回收,真正有效的途径只能是及时释放资源。笨方法之一是不输出 gc 日志,就不会有问题。

与垃圾收集和序列化有关的问题

垃圾收集器与类装入器的交互很密切。在众多的事情当中,收集器检查类装入器的数据结构,来判断哪个类是活动的——也就是说,不应当被当作垃圾收集的。这通常会带来一些意料之外的问题。

场景演示:在这个场景中,序列化以一种意料之外的方式影响了类的垃圾收集(GC):

在这个示例中,SerializationTest实例化了一个URLClassLoader,叫做loader。在装入SerializationClass 之后,对类装入器的引用被取消。想法是希望这样可以允许类装入器装入的类被垃圾收集掉。这些类的代码如清单9和10所示:

清单9.SerializationTest.java

importjava.net.MalformedURLException;

importjava.net.URL;

importjava.net.URLClassLoader;

publicclassSerializationTestextendsClassLoader{

publicstaticvoidmain(Stringargs[]){

try{

URLClassLoaderloader=newURLClassLoader(newURL[]{newURL(

"file://C:/CL_Article/Serialization/dir1/")});

System.out.println("LoadingSerializationClass");

Classc=loader.loadClass("SerializationClass");

System.out.println("CreatinganinstanceofSerializationClass");

c.newInstance();

System.out.println("Dereferencingtheclassloader");

c=null;

loader=null;

System.out.println("RunningGC...");

System.gc();

System.out.println("TriggeringaJavadump");

com.ibm.jvm.Dump.JavaDump();

}catch(MalformedURLExceptione){

e.printStackTrace();

}catch(InstantiationExceptione){

e.printStackTrace();

}catch(IllegalAccessExceptione){

e.printStackTrace();

}catch(ClassNotFoundExceptione){

e.printStackTrace();

}

}

}

清单10.SerializationClass.java

importjava.io.File;

importjava.io.FileOutputStream;

importjava.io.ObjectOutputStream;

importjava.io.Serializable;

publicclassSerializationClassimplementsSerializable{

privatestaticfinallongserialVersionUID=5024741671582526226L;

publicSerializationClass(){

try{

Filefile=newFile("C:/CL_Article/Serialization/test.txt");

FileOutputStreamfos=newFileOutputStream(file);

ObjectOutputStreamoos=newObjectOutputStream(fos);

oos.writeObject(this);

oos.reset();

oos.close();

fos.close();

oos=null;

fos=null;

file=null;

}catch(Exceptione){

e.printStackTrace();

}

}

}

使用Javadump,可以发现类装入器是否被垃圾收集了。如果在类装入器的列表中出现以下部分,就说明它没有被收集:

------a-Loaderjava/net/URLClassLoader(0x44DC6DE0),Shadow0x00ADB6D8,

Parentsun/misc/Launcher$AppClassLoader(0x00ADB7B0)

Numberofloadedclasses1

Numberofcachedclasses11

Allocationusedforloadedclasses1

Packageowner0x00ADB6D8

虽然取消对用户定义的类装入器的引用看起来像是一种确保类被垃圾收集的方法,但实际并不是这回事。在前面的示例中,由于java.io.ObjectOutputStream.writeObject(Objectobj)的使用以及它对GC的影响,所以产生了问题。

在调用writeObject()时(用来序列化SerializationClass),对这个类对象的引用就在内部被传递给ObjectStreamClass并保存在一个查询表中(也就是内部缓存)。保存这个引用是为了加快日后对同一个类的序列化。

当取消对类装入器的引用时,它装入的类就变成无法进行垃圾收集的了。这是因为在ObjectStreamClass查询表中,没有了对SerializationClass类的活动引用。ObjectStreamClass是一个原始类,所以永远不会被垃圾收集。查询表是从ObjectStreamClass中的静态字段引用的,而且保存在类本身之中,而不是保存在实例中。所以,对SerializationClass的引用存在于JVM的生命周期中,所以类就不能被垃圾收集。重要的是,SerializationClass类有一个到其定义类装入器的引用,所以它也不可能完整地取消引用。

为了避免这个问题,凡是要进行序列化的类,都应当由不需要被垃圾收集的类装入器装入——例如由系统类装入器装入。

相关推荐