JVM篇以内存结构(JVM内存模型)

什么是JVM内存区域划分

其实这个问题很是简单,JVM在运行咱们写好的代码时,他是必须使用多块内存空间的,不一样的内存空间用来放不一样的数据,而后配合咱们写的代码流程,才能让咱们的系统运行起来。java

举个最简单的例子,好比我们如今知道了JVM会加载类到内存里来供后续运行,那么我问问你们,这些类加载到内存之后,放到哪儿去了呢?想过这个问题吗?安全

因此JVM里就必须有一块内存区域,用来存放咱们写的那些类。bash

包括咱们定义的成员变量,类变量,方法,局部变量等等,都在jvm内存中对应着一块内存来记录存储。数据结构

方法区

在JDK1.8以前的版本里,表明JVM的一块区域。在1.8版本之后,这块区域的名字改了,叫作“Matespace”,能够认为是“元数据空间”这样的意思,固然这里主要存放的仍是咱们本身写的各类类的相关信息。多线程

举个例子。有以下两个类,User1类没有成员变量,而User2类有一个realName的类变量。并发

public class User1 {
	private String userName = "wangwu";
}
public class User2 {
	private static String realName = "zhangsan";
	private String userName = "lisi";
}

复制代码

这两个类被加载到JVM,就会存放在这个方法区里面(类的全部类变量都会被赋值)。以下图jvm

程序计数器(执行代码指令)

咱们知道,被加载到jvm的类对象是咱们写的.java文件被编译以后的.class文件。ui

在编译事后会将咱们的代码编译成计算机能读懂的字节码。而这个.calss文件就是,就是咱们代码编译好的字节码了。spa

加载到内存之后,字节码执行引擎就开始工做了。去执行咱们编译出来的代码指令,此时问题来了,咱们是否是须要一块内存空间来记录咱们字节码执行引擎目前执行到了哪行代码?这一块特殊的内存区域就是“程序计数器”,就是用来记录当前执行的字节码指令的位置。操作系统

注:多线程环境并发执行的状况下,计算器CPU是必定的,在CPU从线程1切换到线程2,再切换回线程1的时候,是否是要知道线程1执行到哪一步了,这就是程序计数器的做用。所以,当线程再次上下文切换到以前的代码时,就须要一个专门记录当前线程执行到了哪一条字节码。因此,每个线程都有这本身的程序计数器。

栈空间,也成为Java虚拟机栈(本地方法栈不讲,内容差很少,只不过是堆native方法分配的栈空间)

当线程执行到某个方法的时候,若是这个方法有局部变量,那么就须要一块区域来存放局部变量的数据信息。这个区域就叫作java虚拟机栈。

每个线程都有一个本身的java虚拟机栈,好比说当执行main方法的时候就会有一个main线程,用来存放main方法中定义的局部变量

public class TestController {

	public static void main(String[] args) {
	    int i = 1;
    	    User1 user1=new User1();
    	    user1.setUserName("sss");
	}
}
复制代码

好比上面的main()方法中,其实就有一个"user1"的局部变量,他是引用一个User1的实例对象的,还有一个"i"的局部变量. 以下图:

既然是栈,那就遵循后进先出的原则。当方法执行完毕之后,这个栈桢就会出栈,里面的局部变量信息就会从内存删除。因此局部变量是线程安全的。由于只有当前线程能获取到这个值。

Q:为何要用后进先出的数据结构?

A:假设a方法中调用b方法,此时a方法的栈桢先入栈,b方法的栈桢后入栈。当b方法执行完毕后,b方法的栈桢先出栈,继续执行a方法,而后a方法的栈帧再出栈。因此使用一个后进先出的栈结构是很是完美的。

堆空间

仍是上面代码,当main线程执行main()方法的时候,首先在堆内存中实例化User1对象,而后在局部变量中建立user1,user1存的是实例化User1对象的内存地址。而后执行Student对象的getName()方法。

以下图:

堆外内存

其实在JDK的不少底层代码API中,好比NIO。

若是你去看源码会发现不少地方的代码不是java写的,而是走的native方法去调用本地操做系统里面的一些方法,可能调用的都是c语言写的方法。

好比说:

public native int hashCode();

public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);
复制代码

在调用这种native方法的时候,就会有线程对应的本地方法栈,这个其实相似于java虚拟机栈。也是存放各类native方法的局部变量表之类的信息。

还有一块区域,是否是jvm的,经过NIO中的allocateDirect这种API,能够在jva堆外分配内存空间,而后经过java虚拟机栈里的DirectByteBuffer来引用和操做堆外内存空间。

重点关注:方法区,程序计数器,java虚拟机栈和java堆内存

相关文章
相关标签/搜索