公司一位大牛在微博上的一条,打算消化一下,毕竟今后Perm区的上涨还是有可能遇到的。“Java应用Perm区一直呈上涨趋势的原因可以用一个简单的办法排查,就是用btrace去跟踪下是什么地方在调用ClassLoader.defineClass,在大多数情况下这招都是管用的。”
(1)Perm区存放的啥信息?
Perm叫做持久代,存放了类的信息、类的静态变量、类中final类型的变量、类的方法信息,Perm是全局共享的,在一定条件下会被GC掉,方所要承载的数据超过内存区域后,就会出现OOM异常。可以通过-XX:PermSize和-XX:MaxPermSize来指定这个区域的最小值和最大值。持久代的回收主要包括两部分,废弃常量和无用的类,废弃的常量比较容易判别,没有任何String类型的对象引用这个就算可以废弃的了,但是无用的类判别比较复杂,(该类所有的实例已经被回收,JVM中没有任何类的实例;加载该类的ClassLoader被回收;该类对应的java.lang.class没有地方引用)。可以使用-verbose:class以及-XX:TraceClassLoading和-XX:TraceClassUnLoading来查看类的加载和卸载情况。
(2)ClassLoader中的defineClass是干啥的?
该方法用于将二进制的字节码转换为Class对象,对于自定义加载类非常重要,如果二进制的字节码不符合JVM的规范,就会报ClassFormatError,如果生成的类名和二进制中的不符,报NoClassDefFoundError异常,如果加载的class是受保护的,则报SecurityException,如果此类已经在ClassLoader已经加载,会报LinkageError。
例子1:运行时常量溢出
向常量池中添加数据,可以调用String.intern(),这个是native方法,如果常量池中已经存在一个,则返回,否则添加早常量池中。
刚开始的例子如下:
1
2
3
4
5
6
7
8
9
|
public class PermTest {
public static void main(String[] args) {
int i = 0 ;
while ( true ){
String.valueOf(i++).intern();
System.out.println(i);
}
}
} |
发现没有抛出OOM异常,用JConsole查看,(PermSize和MaxPermSize都是10M)在10M的时候曲线又下去了,原来是FullGC把常量池中没有的引用GC掉了,所以没有OOM。
增加了一个List,持有这个引用,就不会被GC掉了。
1
2
3
4
5
6
7
8
9
|
public class PermTest {
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
int i = 0 ;
while ( true ){
all.add(String.valueOf(i++).intern());
}
}
} |
1
2
3
|
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at PermTest.main(PermTest.java: 10 )
|
例子2:用cglib产生代理类,不断的创建类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class PermTest {
public static void main(String[] args) {
while ( true ){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PermTestClass. class );
enhancer.setUseCache( false );
enhancer.setCallback( new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
return proxy.invoke(obj, args);
}
});
enhancer.create();
}
}
static class PermTestClass{
}
} |
1
2
3
4
5
|
Caused by: java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java: 631 )
at java.lang.ClassLoader.defineClass(ClassLoader.java: 615 )
... 8 more
|
Btrace的脚本文件如下(查看哪里调用了defineClass信息):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
@BTrace public class BtraceAll {
@TLS
private static long beginTime;
@OnMethod (
clazz= "java.lang.ClassLoader" ,
method= "defineClass"
)
public static void traceMethodBegin(){
beginTime = timeMillis();
}
@OnMethod (
clazz= "java.lang.ClassLoader" ,
method= "defineClass" ,
location= @Location (Kind.RETURN)
)
public static void traceMethdReturn(
@Return String result,
@ProbeClassName String clazzName,
@ProbeMethodName String methodName){
println( "===========================================================================" );
println(strcat(strcat(clazzName, "." ), methodName));
println(strcat( "Time taken : " , str(timeMillis() - beginTime)));
println( "java thread method trace:---------------------------------------------------" );
jstack();
println( "----------------------------------------------------------------------------" );
println(strcat( "Reuslt :" ,str(result)));
println( "============================================================================" );
}
} |
运行后,发现cglib在创建代理类的时候,确实调用了defineClass方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
=========================================================================== java.lang.ClassLoader.defineClass Time taken : 79
java thread method trace:--------------------------------------------------- java.lang.ClassLoader.defineClass(ClassLoader.java: 615 )
sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 25 )
java.lang.reflect.Method.invoke(Method.java: 597 )
net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java: 384 )
net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java: 219 )
net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java: 377 )
net.sf.cglib.proxy.Enhancer.create(Enhancer.java: 285 )
PermTest.main(PermTest.java: 19 )
---------------------------------------------------------------------------- Reuslt : class PermTest$PermTestClass$$EnhancerByCGLIB$$1ed16944_8
============================================================================ |
至此,问题场景以及如何排查,清晰了。。。
参考书籍:
1、分布式java应用基础
2、深入理解JVM
相关推荐
解决 JVM 中的 PermGen space 问题 PermGen space(Permanent Generation space)是 JVM 中的一块永久保存区域,用于存放 Class 和 Meta 信息。当应用程序加载 Class 时,Class 就会被放入 PermGen space 区域中,...
JVM 内存溢出问题解析 JVM 内存溢出是指程序运行所需的内存大于虚拟机能提供的最大内存的情况。这种情况可能是由于数据量过大、死循环、静态变量和静态方法过多、递归、无法确定是否被引用的对象等原因引起的。同时...
JVM(Java Virtual Machine,Java虚拟机)是运行所有Java程序的假想计算机,是Java程序的运行环境,负责执行指令、管理数据、内存、寄存器等,是实现Java跨平台特性的关键部分。JVM指令手册详细记录了JVM的所有操作...
在Java编程环境中,了解JVM(Java虚拟机)中所有线程的活动状态对于调试多线程程序至关重要。本文将详细讲解如何查看JVM中的线程活动情况,并提供相关示例代码。 首先,Java提供了`java.lang.management....
本文将深入探讨方法区的定义、功能、与其它内存区域的关系,以及其在不同JVM版本中的演进,包括如何设置方法区大小以及如何处理内存溢出问题。 首先,方法区是一种逻辑上的堆内存部分,但在HotSpot JVM中被称为Non-...
java对象在jvm中的存储情况 jvm
JVM规范详细定义了Java可执行代码(即字节码)的格式,包括操作码和操作数的语法和数值、标识符的数值表示方式、以及Java类文件中的Java对象和常量缓冲池在JVM中的存储映像。 这些定义为JVM解释器的开发人员提供了...
标题《JVM调优和故障排除手册》预示着文档将专注于Java虚拟机(JVM)的性能调优以及如何诊断和解决在JVM环境中遇到的问题。JVM是运行Java程序的核心,负责提供内存管理、垃圾回收、多线程处理以及本地方法调用等服务...
标题中提到了JVM原理、JVM调优、JVM内存模型和JAVA并发,这些都是Java虚拟机(JVM)相关的核心概念。JVM是运行Java字节码的虚拟计算机,为Java提供了一个跨平台的环境,确保Java程序可以在不同的操作系统上运行而...
1. **内存监控**:`jvm-mon`能够显示堆内存、非堆内存(如 PermGen 或 Metaspace)的使用情况,包括总大小、已分配、已使用以及剩余空间等信息,帮助开发者发现内存溢出问题。 2. **GC活动追踪**:它会记录垃圾收集...
这个问题通常在Web应用中尤为突出,尤其是当应用包含大量第三方JAR库,其总体积超过了JVM默认分配给PermGen空间的4MB时。这时,就需要通过调整JVM的启动参数来解决。 解决 PermGen 空间溢出的方法是增加JVM内存的...
- 考虑到现代JVM版本已不再使用 PermGen 空间,因此`-XX:MaxPermSize`在较新的Java版本中可能不适用,取而代之的是`-XX:MetaspaceSize`和`-XX:MaxMetaspaceSize`。 通过对这些知识点的理解,开发者可以更好地调整...
Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行字节码,为Java应用程序提供了一个跨平台的运行环境。JDK(Java Development Kit)包含了开发和运行Java程序所需的所有工具,包括JVM。当我们谈论"jdk,jvm...
解决“OutOfMemoryError: PermGen space”问题虽然过程可能比较痛苦,但是一旦找到正确的方法,问题就能得到解决。本文提供的解决方案包括调整PermGen space的大小、优化代码、使用JConsole监控内存等,希望能帮助...
然而,在实际运行过程中,由于复杂的运行环境和技术栈的多样性,JVM可能会遇到各种各样的问题,如性能瓶颈、内存泄漏、CPU占用过高、网络延迟等。这些问题不仅会影响服务的稳定性和响应速度,还可能导致严重的业务...
在加载阶段,JVM通过类的全限定名获取类的二进制数据,并在方法区为这个类分配内存,同时在堆中创建一个代表这个类的java.lang.Class对象。验证阶段是对类文件的格式、元数据、字节码和符号引用等进行检验,确保类...
JVM 输出 GC 日志导致 JVM 卡住是一个常见的问题,尤其是在高并发和高性能应用中。这个问题的根源在于 JVM 的垃圾回收机制(Garbage Collection,GC),它会在 JVM 运行时周期性地进行垃圾回收,以释放内存空间。...
在实际开发过程中,经常会遇到由于JVM内存不足导致的问题,例如`OutOfMemoryError`。本文将详细介绍如何在Eclipse与Tomcat中合理设置JVM参数,以避免此类问题的发生。 #### 二、JVM基础知识简介 Java虚拟机(Java ...
在Java程序运行过程中,最常见的两类内存溢出问题是堆内存溢出(Out of Memory: Heap Space)和 PermGen/Metaspace 溢出。堆内存溢出通常由于对象创建过多,导致堆空间不足以容纳所有对象。解决方法包括增大堆内存、...
在JVM的工作过程中,运行时数据区(也称为Java堆内存)是另一个重要的概念。它分为以下区域: - **堆**:存储所有实例对象和数组。Java垃圾收集器负责管理堆内存,进行对象创建和销毁。 - **栈**:每个线程都有...