JVM内存区域与内存溢出异常总结

JVM内存区域与内存溢出异常总结_第1张图片

JVM内存区域

数据区域 基本作用 线程私有性 溢出异常 备注
虚拟机栈 创建栈帧保存局部变量表,操作数栈,动态链接,方法出口等 线程私有 请求栈深度过深:StackOverFlowError异常,内存不足,OutOfMemoryError异常
本地方法栈 和虚拟机栈基本一致 线程私有 StackOverFlowError 和OOM
程序计数器 字节码执行行号指示器 线程私有 唯一一个没有规定任何OOM的区域
java堆 存放java对象实例 线程共享 OOM 垃圾收集器管理的主要区域
方法区 存储被虚拟机加载的类信息,常量,静态变量等 线程共享 OOM
运行时常量池 方法区的一部分,存放各种字面量和符号引用 线程共享
直接内存 - - OOM -

JVM溢出异常

数据区域 异常溢出
java堆 不断创建对象,并保证对象到GC Roots对象之间有可达路径时会 OOM
虚拟机栈 单线程下,无论是由于栈帧过大还是虚拟机栈容量太小,当内存无法分配时,都抛出StackOverflowError溢出。多线程下,为每个线程的栈分配的内存越大,越容易产生OOM
方法区 运行时产生大量的类去填满方法区,会抛出溢出OOM
本机直接内存 本机内存不足直接溢出OOM

操作数栈和局部变量表的关系

JVM内存区域与内存溢出异常总结_第2张图片

JVM内存区域与内存溢出异常总结_第3张图片

jdk1.8的java内存模型

JVM内存区域与内存溢出异常总结_第4张图片

HotShop虚拟机采用永久代实现方法区。在jdk1.7以后将永久代中的字符串常量池移到堆中。而jdk1.8采用元空间来实现方法区。jdk8不再有永久代

元空间(MetaSpace)和永久代最大的区别就是元空间使用的是本地内存。

JVM内存区域与内存溢出异常总结_第5张图片

jvm三大调优参数

JVM内存区域与内存溢出异常总结_第6张图片

堆和栈的区别

JVM内存区域与内存溢出异常总结_第7张图片

JVM内存区域与内存溢出异常总结_第8张图片

JVM内存区域与内存溢出异常总结_第9张图片

堆和栈的联系

JVM内存区域与内存溢出异常总结_第10张图片

JVM内存区域与内存溢出异常总结_第11张图片

intern

JVM内存区域与内存溢出异常总结_第12张图片

intern在jdk6之前,如果字符串没出现过,那么就把字符串放到常量池(永久代)中,并返回该引用。

jdk6以后,如果字符串没出现过,则在堆中创建该字符串对象,并把该对象的引用存在常量池中(jdK7已将常量池移到堆中)

public class RuntimeConstantPool {
    public static void main(String[] args) {
        String str1 = new StringBuilder("计算机").append("软件").toString();
        System.out.println(str1.intern()==str1);
        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern()==str2);
    }
}

上述代码若采用jdk6运行,则是两个false,因为itern把每出现过的字符串存在永久代中,并返回永久代中该字符串的引用,而StringBuilder创建的字符串实现在堆上,所以必定不同。

jdk7以后,会返回一个true,一个false.因为jdk不会再复制实例,只是在常量池中记录首次出现的实例引用。所以如果字符串是首次出现的话,常量池记录的便是首次出现的字符串的对象实例引用。也就是若,若是首次出现,返回True,否则返回false.

JVM内存区域与内存溢出异常总结_第13张图片

首先,通过引号引用a会将a放进常量池中,而new String(“a”)则会在堆中创建对象。所以s是堆中对象的引用,而s.intern()会尝试把s的副本放进常量池,但此时常量池已经有a了,所以放不进去。所以s2引用的是常量池中的a。所以s和s2不同。

s3引用了堆中对象aa,然后调用s3.intern()会将s3的副本放进常量池,此时常量池中还没有aa,放入成功,之后s4引用常量池中的aa,两者不同。

JVM内存区域与内存溢出异常总结_第14张图片

jdk7后。首先,通过引号引用a会将a放进常量池中,而new String(“a”)则会在堆中创建对象。所以s是堆中对象的引用,而s.intern()会尝试把s的引用放进常量池,但此时常量池已经有a了,所以放不进去。所以s2引用的是常量池中的a。所以s和s2不同。

s3引用了堆中对象aa,然后调用s3.intern()会将s3的引用放进常量池,此时常量池中还没有aa,放入成功,之后s4引用常量池中的aa,此时不管是堆中的aa还是常量池中的aa都是一样的,两者相同。

总得来说,intern在1.6之前,通过str.intern()会将存在堆中的str字符串对象的拷贝存放在字符串常量池中。而jdk1.7后。str.intern()会将堆中str字符串对象的引用放进常量池中。

你可能感兴趣的:(学习总结)