- 浏览: 427156 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
Glogo:
楼主您好,我在试验您的代码的时候发现新开的三个子线程并没有一直 ...
java 高并发 ReentrantLock -- 可重入的锁 -
univasity:
最近发觉也被限速了,投诉一下就好一会~~ 看来明天又要和电信M ...
ADSL上网速度慢 都是帐号限速惹的祸 -
liuyuanhui0301:
java 高并发 ReentrantLock -- 可重入的锁 -
dang_java:
呵.很好的说明文档.
JXTA技术与应用发展 -
helloqidi:
谢谢,学习了
SQL中exists和in的区别
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://fallenlord.blogbus.com/logs/57543373.html
众所周知,Java从1.2开始引入分带GC策略,JVM内存被分成了3个带:young generate、tenured generation和permanent generation
前面两个带相信大家已经非常熟悉了,一般我们所说的GC主要是在这两个带里面运作,我们这里主要讨论Permanent Generation
Perm带是存储类元数据信息的地方,一直以来大家都认为是不会被GC的——确实,类元数据信息被回收了别的类怎么玩?
要说明这个问题,主要需要弄清楚Perm带除了元数据信息外还存了些什么?
栈存基本类型和引用、堆存对象,这个简单的道理大家都懂,但真的是所有的基本类型都存在栈里吗?不见得
还是那句话——无码无真相,我们先从最简单的例子来看一个问题,下面这段代码相信大家都看过很多遍了:
@Test
public void literal() {
String a = "abc";
String b = "abc";
Assert.assertTrue(a == b);
}
没啥好说的,字符串字面量在编译期就会被编译器直接植入.class文件常量池中,并在运行期被JVM当做常量加载,所有存储超过一个字节大小的基本类型都会被编译器优化成这样,这点用javap反编译看下汇编如何压栈的就知道了。关键问题是,常量池在运行期是放在堆里的还是放在栈里的?——答案是都不在
JVM Spec中的Runtime Data Area分为5个区域:pc register、java stack、native stack、java heap、method area,前三个和大多数语言类似比较容易理解,java Heap就是我们常说的堆了,也是Young Generation和Tenured Generation所在,而Method Area就是我们所说的Permanent Generation,上面代码中的字面字符串常量就存在了这里(也有人认为PermGen属于广义上的Heap)
不信?OK,看看下面的代码:
@Test
public void permGenOOM() {
List<String> list = new ArrayList<String>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String t = String.valueOf(i).intern();
list.add(t);
}
}
运行前先设置下JVM参数:-XX:PermSize=2M -XX:MaxPermSize=4M,将PermSize调小点,这样比较容易出结果
执行一下,PermGen应该很快就爆了
这里用到了String的intern方法,作用我就不多说了,如果不了解的可以自己看看JDK API,大致就是将一个字符串变量转存到常量区的String Pool中,和直接写字面常量是一样的效果,以下代码可以证明:
@Test
public void literalAndIntern() {
String a = "abc";
String b = new String("abc");
Assert.assertFalse(a == b);
Assert.assertTrue(a == b.intern());
}
OK,既然知道了Permanent Generation中还存着这个东东,那么我们就可以试验GC了
去掉上一段代码的第3和第6行,也就是整个程序不再持有创建出来的intern对象的引用,使得对象可以被GC,同事在JVM参数中追加GC观察-verbose:gc -XX:+PrintGCDetails
@Test
public void permGenGC() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String t = String.valueOf(i).intern();
}
}
运行代码,这次是不是没有PermGen OOM了?观察Console中的GC日志,看到很多minor GC,我们主要关注Major GC(Full GC)的内容:
[Full GC [Tenured: 340K->340K(4096K), 0.0197170 secs] 959K->340K(5056K), [Perm : 4096K->799K(4096K)], 0.0199235 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
看到Perm段,显示PermGen总大小为4096K,此次full gc将其从4096K清理到了799K
到这里应该已经大功告成了,这篇帖子的主题也达到了——GC会清理PermGen
但事情还不算完,既然GC会去动PermGen,那是否会清理类元数据信息呢?虽然看起来很荒谬的理论,但是还是值得尝试一下的:
@Test
public void permGenCglibOOM() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
createInstance();
}
}
private static ValueObject createInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ValueObject.class);
enhancer.setUseCache(false); // 关闭CGLib缓存,否则总是生成同一个类
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
return (ValueObject) enhancer.create();
}
public static class ValueObject {
private String username = "guolin";
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
这里我们仿造Hibernate用CGLib动态构造了一堆ValueObject的子类,如愿以偿,很快PermGen就OOM了,观察Full GC日志,PermGen一点都没压下去
[Full GC [Tenured: 1950K->1560K(4096K), 0.0323010 secs] 2637K->1560K(5056K), [Perm : 4095K->4095K(4096K)], 0.0323519 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
打开Yourkit Memory 一栏,看到 loaded classes and unloaded classes. 既然有unloaded classes, which means the classes can be unloaded.
来分析一下原因,我们生成的classes,是否是因为被引用才不会full gc 回收?
确实有Classloader,这里是系统自带的application class loader,他会hold住所有的class Object 引用。我们尝试用自己的classloader试试。
ClassLoader cl = null;
for(int i = 0; i < Integer.MAX_VALUE; i++)
{
Enhancer enhancer = new Enhancer();
if(i%100 == 0)
{
cl = null;
cl = new MyClassLoader();
}
enhancer.setClassLoader(cl);
enhancer.setUseCache(false);
enhancer.setCallbackType(MyMethodInterceptor.class);
enhancer.createClass();
}
[Full GC [Tenured[Unloading class net.sf.cglib.empty.Object$$EnhancerByCGLIB$$62f811b3_63]
.......
[Unloading class net.sf.cglib.empty.Object$$EnhancerByCGLIB$$62f811b3_38]
: 1025K->866K(2504K), 0.0152307 secs] 1104K->866K(3080K), [Perm : 4095K->2677K(4096K)], 0.0155104 secs]
这次Full GC的时候有unload classes.
So let me sum up when a class is qulified to be unloaded.
- they will not be unloaded while any instances are still in
existence (have not been GC-ed).
- they will not be unloaded while their owning classloader is still in
instances is still in existence (has not been GC-ed).
- they will not be unloaded while their java.lang.Class object is still
referenced from anywhere (same goes for reflective access to their
members).
发表评论
-
JVM 深入笔记
2012-04-12 20:36 1000JVM 深入笔记(1)内存区域是如何划分的? 一个超短的前言 ... -
JVM启动参数
2011-06-10 16:27 964java [jvmargs] class [arguments ... -
JVM指令集 和 一个异常例子
2011-05-19 16:14 1018指令码 助记符 说明 0x00 nop ... -
The Top Java Memory Problems – Part 1
2011-05-18 11:01 904转载:http://blog.dynatrace.com/20 ... -
BTrace使用简介
2011-04-19 13:51 777该文转载自:http://rdc.taobao.com/tea ... -
大方法的执行性能与调优过程小记
2011-04-18 16:20 804该文章转载自:http://rdc.taobao.com/te ... -
十个最好的Java性能故障排除工具
2011-04-18 16:02 925推荐十个最好的Java性能 ... -
两个OOM Cases排查过程的分享
2011-04-18 15:39 929分享一下两个OOM Cases的查找过程,一个应用是Nativ ... -
Monitoring Java garbage collection with jstat
2011-04-18 15:28 859The original article is at : ht ... -
理解Heap Profling名词-Shallow和Retained Sizes
2011-04-18 14:58 974所有包含Heap Profling功能的工具(MAT, You ... -
Java深度历险(四)——Java垃圾回收机制与引用类型
2011-04-01 08:18 980Java语言的一个重要特性 ... -
Thread Dump 和Java应用诊断
2011-03-30 08:08 1130Thread Dump 和Java应用诊断 ... -
Memory Analysis Part 1 – Obtaining a Java Heapdump
2011-03-14 16:06 1119For troubleshooting Java memory ... -
Java Memory Leaks et al. (2. Act)
2011-03-14 15:37 925The first act of this blog-seri ... -
Erlang memory architecture vs Java memory architecture
2011-03-14 15:36 909I read a really, really interes ... -
The Java Memory Architecture (1. Act)
2011-03-14 15:15 1412One of the biggest strength of ... -
JVM内存模型以及垃圾回收
2011-03-13 09:38 983内存由 Perm 和 Heap 组成. 其中 Heap ... -
Java虚拟机垃圾回收机制
2011-03-13 09:31 825一、相关概念 基本 ... -
JVM结构
2011-03-10 14:43 889类文件格式 JVM使用一 ... -
OOM与JVM(转)
2009-03-24 15:49 966OOM与JVM(转) 2008年08月08日 星期五 15: ...
相关推荐
GC的分代包括Young Generation、Tenured Generation和Permanent Generation。Young Generation又可以分为Eden和Survivor Spaces。 六、GC的调优 GC的调优可以手动进行,也可以使用自适应调优功能。在手动调优中,...
- 永久代(Permanent Generation) 当一个对象被创建时,它首先进入新生代,之后有可能被转移到老年代中。新生代存放着大量的生命很短的对象,因此新生代在三个区域中垃圾回收的频率最高。为了更高效地进行垃圾回收...
这个错误的根本原因在于Java虚拟机(JVM)的永久代(Permanent Generation Space)内存空间不足。永久代主要存储类的元数据,包括类的名称、方法信息、字段信息等。当应用程序加载的类数量过多或者类的元数据占用...
在Java的内存模型中,堆内存被分为新生代(Young Generation)、老年代(Tenured Generation)和持久代(Permanent Generation,Java 8之后被元空间Metaspace取代)。新生代主要用于存放新创建的对象,经过几次垃圾...
Young Generation包含Eden和两个Survivor区,新生对象首先在Eden区分配,然后经过几次垃圾回收后,存活对象逐渐转移到Old Generation,而Permanent Generation主要存储静态类和方法。 针对Bitmap的使用,由于Bitmap...
JVM管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)和Java Stacks(Java栈)。其中,永久保存区域主要存放Class(类)和Meta的信息,Class第一次被...
JVM 的内存模型可以划分为永久代(Permanent Generation)、年轻代(Young Generation)和老年代(Old Generation)三个部分。Heap(堆)= 年轻代(Eden + survivor1+survivor2)+ 老年代。 触发 GC 和 Full GC GC...
* 老年代(Old Generation):包括“Tenured Generation”和“Permanent Generation”。 JVM收集方式 * 一种称为 copying 或 scavenge,将所有仍然生存的对象搬到另外一块内存后,整块内存就可回收。 * 另一种称为...
分代收集策略将堆内存划分为年轻代(Young Generation)、年老代(Tenured Generation)和持久代(Permanent Generation)。年轻代又细分为Eden区、两个Survivor区(From和To)。大多数对象首先在Eden区分配,当Eden...
Heap 分布可以分为三个部分:Young generation、Tenured generation 和 Permanent generation。Young generation 又可以分为 Eden 和 Survivor spaces 两个部分。 7. GC 参数 GC 参数是 GC 调优的重要参数。常见的...
这个错误提示表明,应用程序在运行过程中,内存的永久代(Permanent Generation)空间不足,导致了内存溢出。了解这个问题的原因和解决方案对于优化Java应用的性能至关重要。 PermGen空间,全称为Permanent ...
该算法将对象分为三个代:新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。新生代是用于存储新生的对象,老年代是用于存储生命周期较长的对象,永久代是用于存储类似于Java...
1. **堆(Heap)**:Java对象存储的主要区域,分为新生代、老生代(Tenured Generation)和持久代(Permanent Generation)。 2. **新生代(Young Generation)**:存放生命周期较短的对象,分为伊甸园(Eden Space...
- **永久代(Permanent Generation)** **2.2 新生代(Young Generation)** - **分区**: - **Eden**: 为新对象分配的空间。 - **Survivor Spaces**: 分为两个部分,一个为空,另一个用于存储从Eden区迁移过来...
- **最大永久代大小(Maximum Permanent Generation Size)**:通过`-XX:MaxPermSize`参数设置,在JDK 8及以下版本可用。 - 在JDK 9及以后版本中,永久代被元空间(Metaspace)所替代,相关参数也发生了变化: - ...
首先,JVM内存分为新生代(Young Generation)、老年代(Tenured Generation)和持久代(Permanent Generation,Java 8以后变为Metaspace)。新生代又细分为Eden区、Survivor区(S0和S1)。对象创建时,通常会优先在...
JVM(Java Virtual Machine)内存模型分为多个区域,包括新生代(New Generation)、老年代(Old Generation)和永久代(Permanent Generation)。新生代又细分为Eden区和两个Survivor区(from和to),用于存储生命...
它分为三个主要区域:新域(Young Generation)、旧域(Old Generation)和永久域(Permanent Generation)。新域进一步划分为Eden区和两个辅助生存空间(Survivor Spaces,通常称为From和To空间)。新生成的对象...
- `-XX:PermSize` 和 `-XX:MaxPermSize` 分别设置永久代(`Permanent Generation`)的最小值和最大值,用于存储类元数据。在Java 8及以后版本中,这部分被元空间(`Metaspace`)取代。 2. **线程栈大小**: - `-Xss` ...