`
lifaming15
  • 浏览: 58907 次
  • 来自: ...
文章分类
社区版块
存档分类

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.Heap Size
你的ERROR不是OUTOFMEMEROY,所以HEAP的大小应该可用.-Xms512m -Xmx512m 可以试一试.但是最近4月底有人发表意见说设成相同的比太好,会导致GC运行时间过短,导致效率下降.
3.HeapFreeRatio
这项设置对你的问题有没有帮助不太清楚,做一个比较反差的测试,-XX:MinHeapFreeRatio=10,-XX:MaxHeapFreeRatio=20,看看区别,建议最终设置:
-XX:MinHeapFreeRatio=30,-XX:MaxHeapFreeRatio=70
4.NewRatio
有可能是young generation 的大小过大,导致permanent generation大小不够,所以调整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


import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

publicclassSerializationTest extends ClassLoader {

public static void main(String args[]) {
try {
URLClassLoader loader = new URLClassLoader(new URL[] { new URL(
"file://C:/CL_Article/Serialization/dir1/") });
System.out.println("Loading SerializationClass");
Classc = loader.loadClass("SerializationClass");
System.out.println("Creating an instance of SerializationClass");
c.newInstance();
System.out.println("Dereferencing theclassloader");
c = null;
loader = null;

System.out.println("Running GC...");
System.gc();
System.out.println("Triggering a Javadump");
com.ibm.jvm.Dump.JavaDump();

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}



清单 10. SerializationClass.java


import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

publicclassSerializationClass implements Serializable {

private static final long serialVersionUID = 5024741671582526226L;

public SerializationClass() {
try {
File file = new File("C:/CL_Article/Serialization/test.txt");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(this);
oos.reset();
oos.close();
fos.close();
oos = null;
fos = null;
file = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}



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


------a- Loader java/net/URLClassLoader(0x44DC6DE0), Shadow 0x00ADB6D8,
Parent sun/misc/Launcher$AppClassLoader(0x00ADB7B0)
Number of loaded classes 1
Number of cached classes 11
Allocation used for loaded classes 1
Package owner 0x00ADB6D8



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

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

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

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics