深入JAVA虚拟机之运行时数据区

前言
最近在啃一本书《深入JAVA虚拟机》,这本书不是第一次看,可以说是从大学就开始看,
这一次应该算第三次啃这本书,也应该说算是第一次真正啃这本书。大学的时候,只是好奇表层的一些神奇现象,随着工作几年后,现在回过头来再次啃这本书,对于表层的那些以前觉得神奇的现在已经感觉乏味,反而对于底层是如何实现、如何运作的越来越着迷。这也是这次看这本书的初衷。通过写博客记录下自己的学习过程,也方便以后回头看看现在的看法想法在将来会变成怎样。如果我在下面的文字表述上或者理解上有误解或者错误,请各位大神能够留言指正,如果有跟我一样爱好的同学,也欢迎留言探讨,相互学习。


概述

       对于c/c++开发者而言,他们对内存管理拥有最高权利,但是需要干最多“活”的劳动
人民。他们担负者每一个对象生命周期从开始到结束的责任(需要分配内存,回收内存)。
对于java开发者而言,这些苦逼的活不需要做,因为都交给JVM的**自动内存管理机制**
去做了。有句调侃的话叫:人生苦短,我用XXXX.那么java开发者真就轻松了吗?
不!虽然把内存管理交给JVM去做了,但要是程序一旦出先内存泄漏、内存溢出等问题,
如果不清楚虚拟机是如何使用内存的,那么排除错误将会比他们更加苦逼。

运行时数据区

java虚拟机在执行程序过程中,会把**管理的内存**,划分为多个**数据区**。它们都有特
定的用途。包括:方法区,堆,程序计数器,虚拟机栈,本地方法栈,运行时常量池,直
接内存

1.程序计数器

它是一块很小的内存。作用可看作是:当前线程所执行的字节码的行号指示器。字节码解
释器就是通过它获取一条需要执行的字节码指令。java虚拟机多线程是通过线程轮流切换
并分配处理器时间来实现的,在确定的时间一个处理器,只会执行一条指令。所以为了线
程在切换时能够回到在正确的位置,每一个线程都有独立的程序计数器,是当前线程私有
的。各个线程之间的计数器是互不影响,独立存储的,称之为线程私有的内存区域。
    特点:
        线程间互不影响,独立存储。

2.虚拟机栈

它也是线程私有的,生命周期与线程相同,是java方法执行时的内存模型。每个方法执
行时,都会创建一个栈幀,用于存储局部变量表、操作栈、动态链接、方法出口等信息。
局部变量表存放了编译期可知的基本数据类型,对象引用和return Address类型。

3.本地方法栈

为虚拟机使用native方法服务。它的作用和虚拟机栈非常相似,不同点就是服务的对象
不同,前者是给java方法服务,而后者是给native方法服务。

4.java堆

是java虚拟机中管理最大的一块内存区域。是被所有线程所共享的内存区域。在虚拟机
启动时创建,唯一目的就是存储对象实例。是java垃圾收集器的主要管理区域,这里又
被成为GC堆。从内存回收来看,现在的垃圾收集器实现大都是使用分代收集算法,
所以java堆还可以细分为:新生代,老年代,再细分为:eden空间,from servivor空间,
To Servivor空间。该片内存,可以在物理上不是连续的,只要逻辑上连续即可。
可动态扩展的(-Xms,-Xmx)。

5.方法区

所有线程共享,用于存储虚拟机加载的类信息、常量、静态变量、即使编译的代码等数据。

运行时常量池
是方法区的一部分。class文件内除了有类的版本信息、字段、方法、接口等描述信息外,
还有一项常量池。这个常量池用于存放编译期生成的各种字面量和符号引用,在类加载后
存放到方法区。

6.直接内存

并不是运行时数据区内存中的一部分,也不是虚拟机规范定义的内存,因为频繁使用,也
可能出现OutOfMemoryError异常。在jdk1.4加入的NIO类,引入了一种基于通道/缓冲区
的I/O方式,它可以使用native函数库直接分配堆外内存,通过存储在java堆中
的DirectByteBuffer对象作为这块内存的引用进行操作。

图示:
深入JAVA虚拟机之运行时数据区

总结:
    1.程序计数器,是线程私有的,线程间相互独立,是唯一一个不会抛出OutOfMemoryError异常的运行时数据区。
    2.虚拟机栈,是线程私有的,与线程生命周期相同,是方法执行时的内存模型,通常说的栈内存指的就是这个
    虚拟机栈。
    3.本地方法栈,与虚拟机栈非常相似,服务对象不同,一个是服务Native方法,一个是服务Java方法。
    4.Java堆,最大的一块内存,线程共享区,虚拟机启动时创建,垃圾收集器的主要用武之地,所以这里又叫
    GC堆。可动态扩展。
    5.方法区,所有线程共享,记录虚拟机加载的类信息,常量、静态变量、即时编译的代码。

现在也已经清楚了运行时数据区的职责划分,那么现在可以开始讨论java是如何访问对象的。

  • 对象访问

java中对象访问随处可见,那么你知道它是怎么实现访问对象的吗?在java中就算最简单的对象访问都必然会
涉及到java栈、java堆、方法区这三个最重要的内存区域。举例如下:

Object obj = new Object();

假设上面语句出现在一个方法体内,Object obj会被反映到java栈,作为一个reference类型数据出现。
而new Object()会反映到java堆中,在java堆中,开辟出一块存储object类型实例数据值的结构化内存,java堆中
还必须要保存对象类型数据的地址信息,而对象类型数据就保存在方法区内。

但是因为reference类型在java虚拟机规范中说指向一个对象的引用,并没有说明该引用通过什么方式去定位和如何访问java堆中的对象具体位置。根据不同的虚拟机实现,会有不同的方式,主流的方式就两种:句柄、直接指针
下面是两种方式的示意图:

深入JAVA虚拟机之运行时数据区
深入JAVA虚拟机之运行时数据区

猜你喜欢

转载自blog.51cto.com/4837471/2157417
今日推荐