【JAVA 虚拟机的内存模型】

1.这个话题其实被很多人写过了,但是也会被很多人继续写下去,我就是其中一个。

2.JVM的内存划分其实并不是一样的,因为每个虚拟机,每个版本都会有一定的改动。本文讨论的是HotSpot虚拟机的内存模型(JAVA虚拟机的一个版本)。

以上就是JVM的内存结构。

一个一个来说。

首先提两个概念:线程私有和线程共享。这个指的是在多线程的环境下,如果一个线程的某些资源只能是自己访问,其他线程不可访问,那么就是线程私有,相反,如果所有线程共享某份资源就是线程共享。

(1)程序计数器,(Program Counter Register)。这个在机组里面其实有很到位的解释,当然了这里其实和机组里面的是差不多的。因为JVM主要是解释字节码的,所以这个计数器的作用就是标识当前线程解释到了哪一行字节码,通过改变这个计数器的值来让程序不断地执行。这块内存肯定是线程私有的,因为每个线程解释到的位置是不尽相同的,所以这个具有线程的特殊性,必须线程私有。

(2)JAVA虚拟机栈(JAVA Virtual Machine Stacks)。虚拟机栈其实是描述JAVA方法执行的内存模型,这个很好理解,比如当前线程里面有很多函数执行,那么这些函数需要压栈,出栈等。这些操作都是在这个虚拟机栈里完成的。这个也是线程私有的。

(3)本地方法栈。(Native Method Stack)。这个其实是为了本地方法而设置的栈。什么是本地方法?接下来讨论这个问题。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

JAVA中的Native方法:

1.native是java中的一个关键字,表明该方法是一个native方法。那么什么叫做native方法呢?

native方法也叫做本地方法,是该系统提供并且实现的方法,jvm只需要调用即可。

    众所周知,java跨平台的基础就是因为有了java虚拟机,jvm就是一个翻译官,可以把字节码给翻译出来。但是,这样的特性,也让java失去了对底层的操作能力,因为需要跨平台,当然就不能直接操作底层,要不然移植到别的平台上很有可能就会不兼容。

    然而有些操作是必要的,比如一些系统调用,这如何实现?

2.JNI:为了解决这些问题,java提供了一种和其他语言交互的接口机制:java native interface。java 本地方法接口,通过这个机制,我们可以用java调用本地实现的方法,然后再用本地方法操作底层。这样就实现了java操作底层的目标

     当然了,一般都是和c++/c交互。具体的方式就是:把当前方法做成dll,java调用这个dll。

                                 

r然后再当前目录下执行javac ,编译出字节码文件,再用javah,编译出头文件。编译出的头文件:

                               

使用这个头文件,做一个动态链接库。具体做法就是新建一个.c,包含这个头文件,实现HelloJNI方法:

               

把这个.c文件,编译成动态链接库,把这个库放到当前目录下,或者直接放在system32里,再去执行java代码,就可以成功。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

回到刚才的话题,其实可以看出来,native方法就是本地实现的方法,然后java通过JNI调用。那么本地方法执行的时候也是需要栈的,所以才有本地方法栈。当然,本地方法栈也是线程私有的。

(4)java 堆(java heap):堆其实是虚拟机中最大的一块,并且堆是所有线程共享的。这里存放着所有的对象实例以及数组。这里也是垃圾回收主要关心的区域。所以从内存回收的角度来看,会分为几个区:粗粒度一点就是新生代和老年代,细粒度一点就是:Eden,From Surrvivor,To Surrvivovr.堆的大小是有限的,并且物理储存可以不连续。

(5)方法区:这里储存的是已经被虚拟机加载后的信息,常量,静态变量等。这里是线程共享的。方法区一般是不可变的,因此也被称为永久代,当然,二者事实上并不是等价的。方法去里面有一个区叫做运行时常量池,这里存放的是编译器的字面常量,符号引用,以及已经被虚拟机加载过的类的信息。当然了,对于非类文件来说,并不是只有编译期才会被放入常量池,java提供了intern()方法,只要是该变量实现了这个方法,可以调用这个方法把它放到常量池中去。

以上就是五个区的划分,其实只要是清楚了他们到底是干什么的,性质自己分析也能得到。

发布了370 篇原创文章 · 获赞 48 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/103465168