JVM 运行时数据区域 主要分为两大部分:
1.线程私有 (即所谓的线程安全) :
①程序计数器:当前线程所执行的字节码(即.class文件,因为所有.java文件都需要编辑成.class文件才能运行。当然不一定就是.java文件,这里泛指所有能够在JVM上跑的程序。如)的行号指示器。JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的。这个计数器就是为了线程切换后能恢复到正确的执行位置。
②JVM栈:Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈针在jvm中入栈到出栈的过程。一直会有人这么说,Object obj = new Object () ,这里obj分配在栈上,但为什么呢?想必大部分举例时都是在main方法里面写的。那么其实我想说的是main也是个方法,执行的时候当然也会创建一个栈帧。当然在其他正常的方法里面的话更好理解了。TODO----
局部变量表存放了编译期间可知的各种基本数据类型(boolean、byte、short、char、int、float、double、long)、引用类型(可能是指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄)、returnAddress类型。
③本地方法栈:类似于JVM栈,jvm栈为java方法即字节码服务,本地方法栈为Native方法服务。
2.线程共享:
①java堆:所有对象实例以及数组(数组难道不是对象吗?)都在堆上分配(但并不是绝对的,因为逃逸分析的成熟)
逃逸分析:一个对象在方法中被创建时,当它作为参数传递给其他方法时,称为方法逃逸。当它赋值给类变量或者其他线程中的实例变量时,称为线程逃逸。这时我们就可以进行逃逸分析,如果一个对象即不是方法逃逸,又不是线程逃逸。那么我们就可以进行高效优化:1、栈上分配。2、同步消除。3、标量替换。标量指的是一个数据无法分解成更小的数据。也就是说可以将一个对象分解成其中一个或者几个字段,当然前提是这个对象不会逃逸。
java堆是GC(垃圾收集器)管理的主要区域。当代收集器都是采用分代收集方法。所以java堆分为新生代,年老代。新生代又分为Eden区和Survivor区,Survivor区又分为From Survivor区和To Survivor区。
②方法区:存储已被虚拟机加载进来的类信息、常量、静态变量、编译后的代码。
比如Classloader引用、运行时常量池、字段数据、方法数据(即方法签名)、方法代码。
③运行时常量池:运行时常量池是方法区的一部分。用于存放编译期生成的各种字面量和符号引用(字段引用、方法引用),这部分内容将在类加载后进入方法区的运行时常量池中存放。它的一个重要特征是具有动态性:运行期间也可以将新的常量放入池中。例:String类的intern()方法。
直接内存:直接内存不是虚拟机运行时数据区的一部分。但是还是会引发OutOfMemoryError异常出现。在JDK 1.4中新加入了NIO。引入了一种基于通道(Channel)与缓冲区(Buffer)的I/0方式,它可以使用Native方法函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存引用进行操作。这样可以提高性能,因为避免了在java堆和Native堆中来回复制数据(这里有点不太明白)。个人理解是java堆是属于一个进程(即jvm进程)的。而直接内存是OS本地内存。