一、java内存模型和java运行时数据区域的关系:主内存对应着java堆,工做内存对应着java栈。java
二、volatile
关键字,使得变量的更新在各个工做内存中都是实时可见的。在DCL的单例模式中有运用到!面试
由于jvm的运行时数据区域一直在改善,因此不一样jdk版本之间会有不一样。算法
一、程序计数器的做用,由于.java文件被编译成.class文件,它做为当前线程所执行的字节码的行号指示器。当字节码解释器工做时,就是经过改变这个计算器的值来选取下一条要执行的字节码指令。每条线程都有一个独立的程序计数器。数组
二、本地方法栈就是执行本地native方法的栈,native方法由虚拟机实现!jvm
三、java虚拟机栈描述的是该线程执行java方法(method)时的内存模型。每个方法都对应一个栈帧,栈帧中的局部变量表存储了方法中的基本数据类型变量、对象引用变量。函数
如上图所示,局部变量表保存了方法中声明的8种基本类型变量和对象引用变量。每个栈帧中还有一个指向运行时常量池的引用,这是指String类型。 下面有一个经典的String对象生成的面试题!
四、java堆是JVM中内存最大的一块,被全部线程共享。几乎全部的对象实例都在这里分配,因此java堆也是JVM垃圾回收的主要区域。java堆又被分红了年轻代,老年代;年轻代进一步能够划分为Eden空间,From Survivor空间、To Survivor空间。性能
当咱们使用new关键字分配对象时,就是在java堆中生成对象。
下面分析一下对象生成时的状况。测试
年轻代使用的是复制算法:每次Minor GC把Eden区和一块Survivor区的存活对象复制到另外一块Survivor区。老年代使用的是标记-整理算法:每次Major GC把存活对象都想内存空间的一端移动,而后直接清理掉端边界之外的内存。spa
大对象如数组、很长的字符串,直接进入老年代空间。
五、方法区用于存储JVM加载的类信息、final常量、static静态变量等数据,方法区中的数据都是整个程序中惟一的。方法区还包含了运行时常量池,主要存放编译期生成的字面量和符号引用(在类加载后放入)。String对象的字面量就会被放入到运行时常量池中。操作系统
垃圾回收在方法区主要是对常量的回收和对类型的卸载。
参考文章:java8的jvm内存区域。
元空间和永久代的性质是同样的,都是对JVM方法区的实现,做用是同样的。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机JVM内存中,而是使用本地内存。
为何用元空间取代永久代呢?
JDK1.4以后加入的NIO,引入了基于通道channel和缓冲区buffer的IO,直接使用native函数分配堆外内存,显著提升IO性能,避免了原来BIO的在java堆和naive堆中来回复制数据。
参考文章:字符串常量池。
下面来分析一下咱们常见的生成对象或基本数据类型变量的内存模型。这样能够对JVM有一个更好的理解。
int i =3;
,一个方法对应一个栈帧,方法中的基本数据类型变量直接在栈帧中分配。若是是static、final类型的基本数据类型则存储在运行时常量池中,和String同样。Object o1 = new Object();
,对象引用(Object o1
)存储在栈帧中,可是对象数据(new Object()
)存储在java堆中,对象类型数据(Class等信息)存储在方法区中。String s1 = new String("abcd");
,使用new声明的对象,对象引用(String s1
)存储在栈帧中,对象数据(new String(“abcd”)
)存储在java堆中,字符串值(“abcd”
)存储在运行时常量池中。String s2 = “abc”
,对象引用(String s2
)存储在栈帧中,字符串值(“abc”
)存储在运行时常量池中。java栈、java堆、方法区这3者之间的关系大概就是上面的分析所示。
OutOfMemoryError
若是java堆中分配的对象太多,且GC后内存空间仍是不够用。下面经过循环生成对象来消耗内存空间进行测试。
相关指令:VM Args: -Xms20m -Xmx40m
,表示JVM分配的堆内存最小为20MB,最大为40MB。
public static void main(String[] args) { while (true) { List<Object> list = new ArrayList<>(10); list.add(new Object()); } }
StackoverflowError
若是java栈的栈深度大于JVM容许的深度,就会抛出该错误。下面经过无限递归调用来进行堆栈进行测试。
相关指令:VM Args: -Xss128k
,表示JVM分配的栈容量为128KB。
public class StackOOM { private int length = 1; public void stackLeak() { length++; stackLeak(); } public static void main(String[] args) { StackOOM stackOOM = new StackOOM(); stackOOM.stackLeak(); } }