0%

JVM-自动内存管理

一、自动内存管理:

JVM运行时数据区图

jvm1

1、程序计数器:
占用较小的空间,可以看作当前线程所执行字节码的行号指示器。分支,循环,跳转和异常处理都通过程序计数器来完成。
线程切换时恢复到字节码执行位置也依赖程序计数器,每个线程都有自己的程序计数器。

2、Java虚拟机栈
Java虚拟机栈是线程私有的,描述的是Java方法执行的内存模型。每个方法在执行时都会创建栈帧,用于存储操作数栈,局部变量表,动态链接和方法出口等信息;每个方法的从调用到执行完成,对应着一个栈帧的入栈到出栈的过程。
局部变量表存储基本的数据类型和引用类型,一般在编译时确认大小,在方法执行中不会改变局部变量表的大小;

3、本地方法栈
为虚拟机用到的本地方法服务。
4、Java堆
在虚拟机启动时创建,存放对象的实例,是线程共享区域。根据分代垃圾收集算法,还分为新生代和老年代。
5、方法区
用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。对HotSpot虚拟机来讲,也称为永久代(PerGen)。
运行时常量池属于方法区的一部分,Class文件除了描述类版本,字段,方法等外,还有常量池部分,用于存储编译时期生成的字面量或版本号引用。
常量池可以在代码运行时加入,比如String类intern()方法。
6、直接内存
直接内存(Direct Memory)不是运行时数据区的一部分,也不是JVM规范的中的定义内存。
在NIO中的基于通道和缓冲区的IO方式,它直接使用Native函数分配堆外内存,然后通过存储在Java堆中的DirectBuffer对象作为这块内存的引用进行操作。

二、对象创建访问

1、对象的创建(Hotspot虚拟机)
对象创建过程:
new指令->常量池中定位类的符号引用->类是否加载、解析、初始化->为对象在堆上分配内存->执行方法
对象的内存布局:
对象头(Header):运行时数据(Hashcode、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳)
32/64位

jvm2

实例数据(instance data):
程序定义的各类型的数据内容。
对齐填充(Padding):
对象的大小必须是8位的整数倍,不足则补齐;

2、对象的访问:
方法一、垃圾回收时只改变句柄中的实例数据指针

jvm3

方法二、速度快,Hotspot虚拟机使用。

jvm4

三、OutOfMemoryError:

1、堆溢出:
运行下列代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
*

* VMArgs -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=e:\java\dump
* @author weining
*
*/
public class HeadOOM {
static class OOMObject{

}

public static void main(String[] args){
List<OOMObject> list = new ArrayList<OOMObject>();
while(true){
list.add(new OOMObject());
}
}
}

输出了以下错误:

1
2
3
4
5
6
7
8
9
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid2408.hprof ...
Heap dump file created [10875924 bytes in 0.297 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at oom.HeadOOM.main(HeadOOM.java:21)

使用Eclipse Memory Analyzer工具分析dump文件
MAT的地址为:http://www.eclipse.org/mat/downloads.php
eclipse插件升级地址为:http://download.eclipse.org/mat/1.5/update-site/

2 、虚拟机栈溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**

* VM ARGS:-Xss128k
* @author weining
*
*/
public class JavaVMStackSOF {
private int stackLength = 1;

public void stackLeak(){
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try{
oom.stackLeak();
}catch(Throwable e){
System.out.println("stack length=" + oom.stackLength);
throw e;
}

}
}

运行后:

1
2
3
4
5
6
stack length=21824
Exception in thread "main" java.lang.StackOverflowError
at oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
at oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
at oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
at oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)

3、方法区和运行时常量池溢出
1)、常量池溢出
代码:

  • /**
    
     * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10m
     * @author weining
       *
        */
       public class RuntimeConstantPoolOOM {
        public static void main(String[] args) {
         List<String> list = new ArrayList<String>();
         int i = 0;
         while(true){
       list.add(String.valueOf(i++).intern());
         }
        }
       }
    
    1
    2
    3
    4
    5
    6
    7

    运行后:

    ```java
    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)
    2)、方法区溢出 加载过多的类信息,常见spring框架,JSP编译等。
    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
    /**

    * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10m
    * @author weining
    *
    */
    public class JavaMethodAreaOOM {
    public static void main(String[] args) {

    while(true){
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(OOMObject.class);
    enhancer.setUseCache(false);
    enhancer.setCallback(new MethodInterceptor(){

    @Override
    public Object intercept(Object obj, Method method,
    Object[] args, MethodProxy proxy) throws Throwable {
    return proxy.invokeSuper(obj, args);
    }

    });
    enhancer.create();
    }
    }

    static class OOMObject{

    }
    }
    运行时出现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
    at oom.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:31)
    Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
    ... 3 more
    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
    4、直接内存溢出 通过-XX:MaxDirectMemorySize指定,否则=-Xmx NIO代码中出现,dump文件较小。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    *VM Args:-Xmx20m -XX:MaxDirectMemorySize=10m

    * @author weining
    *
    */
    public class DirectMemoryOOM {
    private static final int _1MB = 1024*1024;

    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
    Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible(true);
    Unsafe unsafe = (Unsafe)unsafeField.get(null);
    while(true){
    unsafe.allocateMemory(_1MB);
    }

    }
    }
    运行后:
    1
    2
    3
    Exception in thread "main" java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at oom.DirectMemoryOOM.main(DirectMemoryOOM.java:21)