Java内存模型(JMM),全称 Java Memory Model,中文释义Java内存模型;
对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像C/C++程序开发程序员这样为每一个 new 操作去写对应的 delete/free操作,不容易出现内存泄漏和内存溢出问题。正是因为 Java 程序把内存控制权利交给 JVM虚拟机。一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会是一个非常艰巨的任务。
JVM虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。
程序计数器是一块较小的内存空间,是当前线程所执行的字节码的行号指示器。
程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它随着线程的创建而创建,随着线程的结束而死亡。
与程序计数器一样,VM Stack虚拟机栈也是线程私有的,它的生命周期和线程相同,用于描述 Java 方法执行时的内存模型,每次方法调用的数据都是通过栈传递的。
Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。
每一次方法调用都会有一个对应的栈帧被压入 VM Stack虚拟机栈,每一个方法调用结束后,代表该方法的栈帧会从VM Stack虚拟机栈中弹出。并且在JVM执行引擎运行时, 所有指令都只能针对当前活动栈帧进行操作。虚拟机栈通过 pop和 push的方式,对每个方法对应的活动栈帧进行运算处理,方法正常执行结束,肯定会跳转到另一个栈帧上。
异常类型:
Java 虚拟机栈会出现两种错误:StackOverFlowError 和 OutOfMemoryError。
● StackOverFlowError: 当线程请求栈的深度超过 JVM虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
● OutOfMemoryError: JVM的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
本地方法栈用于虚拟机调用的 Native方法。
native关键字修饰的本地方法被执行的时候,在本地方法栈中也会创建一个栈帧,用于存放该native本地方法的局部变量表、操作数栈、动态链接、方法出口信息。方法执行完毕后,相应的栈帧也会出栈并释放内存空间。也会出现 StackOverFlowError和 OutOfMemoryError两种错误。
Heap堆区,用于存放对象实例和数组的内存区域。
Heap堆是JVM 所管理的内存中最大的一块区域,被所有线程共享的一块内存区域。堆区中存放对象实例,“几乎”所有的对象实例以及数组都在这里分配内存。
Heap堆是垃圾收集器GC(Garbage Collected)管理的主要区域,因此堆区也被称作GC 堆(Garbage Collected Heap)。
从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 JVM中的堆区往往进行分代划分,例如:新生代 和 老年代。目的是更好地回收内存,或者更快地分配内存。
用于存放类信息、常量、静态变量、JIT即时编译器编译后的机器代码等数据等。例如:java.lang.Object类的元信息、Integer.MAX_VALUE常量等。
JDK1.6:
JDK1.7:
JDK1.8:
1.String的两种创建方式:
● 第一种方式是在常量池中获取字符串对象;
● 第二种方式是直接在堆内存空间创建一个新的字符串对象;
// 先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd"
String str1 = "yangshulin";
String str2 = new String("yangshulin"); //堆中创建一个新的对象
String str3 = new String("yangshulin"); //堆中创建一个新的对象
System.out.println(str1==str2); //false
System.out.println(str2==str3); //false
2.String的intern() 方法:
检查指定字符串在常量池中是否存在?如果存在,则返回地址,如果不存在,则在常量池中创建;
String s1 = new String("yangshulin");
String s2 = s1.intern(); // 查看字符串常量池中是否存在"yangshulin",如果存在则返回地址,如果不存在,则在常量池中创建
String s3 = "yangshulin"; // 使用常量池中的已有字符串常量"yangshulin"
System.out.println(s2 == s3); // true,地址相同
3.String的拼接:
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing"; // 常量池中的新字符串对象
String str4 = str1 + str2; // 在堆中创建的新字符串对象
String str5 = "string"; // 常量池中的已有字符串对象
System.out.println(str3 == str4); //false
System.out.println(str3 == str5); //true
System.out.println(str4 == str5); //false
4.String s1 = new String(“abc”);这句代码创建了几个字符串对象?
创建 1 或 2 个字符串。
如果常量池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc”。如果常量池中没有字符串常量“abc”,那么它将首先在池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。