虚拟机自动内存管理机制,不用像C和C++那样为每一个new操作去写对应的delete/free操作,不容易出现内存泄漏和内存溢出问题。
运行时数据区域:
JDK1.8之前:线程共享:堆,方法区(运行时常量池)。线程私有:虚拟机栈,本地方法栈,程序计数器。线程共享:直接内存。
线程私有:1.程序计数器:实现代码的流程控制,多线程情况下,记录当前线程执行的位置。2.Java虚拟机栈:由一个个栈帧组成,每个栈帧中都拥有局部变量表,操作数栈,动态链接,方法出口信息。局部变量表中主要存放了编译期可知的各种数据类型,对象引用。每一次的函数调用都会有一个栈帧压入栈,每一个函数结束都有一个栈帧出栈。3.本地方法栈:为虚拟机使用到的native方法服务,本地方法执行时也会再本地方法栈创建一个栈帧,用于存放该本地方法的局部变量表,操作数栈,动态链接,出口信息。
线程共享:堆:在虚拟机启动时创建,唯一目的就是存放对象实例,几乎所有的对象实例和数组都在这里分配内存,Java堆还可以细分为新生代和老年代。
方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译的代码等,也被称为永久代。
运行时常量池也是方法区的一部分,class文件中除了有类的版本,接口等信息外,还有常量池表。
为什么要将永久代替换为元空间呢?整个永久代有一个JVM设置的固定大小,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间可能溢出,但是比原先出现的几率会更小。
对象的创建:类加载检查--分配内存--初始化零值--设置对象头--执行init方法
step1:类加载检查,虚拟机遇到一条new指令时,将去检查这个指令的参数能否在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程
step2:分配内存,当类加载检查通过后,为新生对象分配内存。
step3:初始化零值,虚拟机将分配到的内存空间都初始化零值,这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就可以使用。
step4:设置对象头:初始化零值之后,虚拟机要对对象进行必要的设置,假如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄信息等。这些信息存放在对象头中。
step5:执行init方法,从程序员角度来看,init方法未执行,所有字段都还为零,所以一般来说,执行new指令后接着执行init方法,按照程序员意愿初始化对象,这样一个真正的对象才算完全创建出来。
对象的访问定位:句柄,直接指针