学习之路:JVM从入门到放弃——第一章:内存结构

JVM内存结构

jvm内存结构分为三大块:线程私有区域线程共享区域直接内存
线程私有区域线程共享区域又被称之为运行时数据区,也就是我们常说的内存。概念:jvm在运行过程中会把它所管理的内存划分成若干不同的数据区域
学习之路:JVM从入门到放弃——第一章:内存结构

线程私有区域

线程私有:是拿来运行指令的。
线程私有区域下又包含三块:程序计数器本地方法栈虚拟机栈
java天生就是多线程的,在程序运行时可能会启动多个线程,每启动一个线程,该线程必定包含程序计数器、本地方法栈、虚拟机栈。

程序计数器: 因为java程序是多线程的,它必须要有一个东西来指向当前线程正在执行的字节码指令地址(行号)。它也是虚拟机内存中唯一不会报OOM(内存用完)错误的区域。
延伸: 为什么需要程序计数器?
答:Java是多线程的,多线程就意味着线程切换,它的作用:确保程序在多线程情况下能够正常执行。

栈(Stack): 实际上就是一种数据结构(数组、链表也是数据结构),该数据结构的特点:入口和出口只有一个,有先进后出的特征,并且操作上只有入栈、出栈两种方式。
先进后出(First In Last Out):实际上就像仓库装货物一样,从下至上一个一个的叠起来,当在使用的时候,从上到下一个一个的使用。
延伸: 为什么JVM要使用栈?
答:使用栈这种数据结构会非常好的兼容方法调用方法的特点。

虚拟机栈(大小设置:-Xss 1M): 存储当前线程运行方法所需要的数据、指令、返回地址。 将虚拟机栈进一步划分成栈帧,栈帧再次细化为:局部变量表操作数栈动态链接返回地址等。
学习之路:JVM从入门到放弃——第一章:内存结构
栈帧: 因为一个程序可能包含很多很多线程(单个线程就可以理解为虚拟机栈),而一个线程也可能包含很多很多个方法。栈帧就可以理解为在虚拟机栈下的单个方法,即:栈帧就是单个方法。
局部变量表:其实就是这个方法里存储局部变量的表。
操作数栈:在这个栈帧里,需要完成入栈和出栈指令的操作。
动态链接:当前这个栈帧(方法),调用其他的方法,实际上就是调用其他方法的直接引用,这个直接引用就是动态连接。
返回地址:记录这个方法的返回。
延伸:栈和虚拟机栈有什么关系?
答:栈是一种数据结构,虚拟机栈就是在这种数据结构上的一种拓展。

本地方法栈:本地方法栈保存的是native方法的信息,当一个jvm创建的线程调用native方法后,jvm不再为其在虚拟机栈中创建栈帧,jvm只是简单的动态链接并直接调用native方法。
延伸:虚拟机规范无强制规定,各版本虚拟机*实现。Hotspot直接把本地方法栈和虚拟机栈合二为一。

线程共享区域

线程共享:是拿来处理数据(存储数据)的。
线程共享和线程没有关系,无论启动多少个线程,线程共享区域只会有:方法区
方法区(永久代(JDK1.7及之前)、元空间(1.8及以后)),方法区的组成:类信息、常量、静态变量、即时编译后的代码
Java堆(-Xms,-Xmx,-Xmn):堆是需要重点关注的区域,涉及到内存的分配(new关键字,反射等)与回收(回收算法、收集器等)
“-Xms”:最小的空间大小;
“-Xmx”:最大的空间大小;
“-Xmn”:最新的空间大小;

JVM各版本内存区域的变化

运行时常量池:class文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放进这个区域。组成:符号引用、字面量
JDK1.6: 运行时常量池在方法区中。
JDK1.7: 运行时常量池在堆中。
JDK1.8: 去掉永久代,使用元空间(空间大小只受限于机器的内存)替代永久代。

直接内存

直接内存:不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。
1.如果使用了NIO,这块区域就会被频繁使用,在Java堆内可以用directByteBuffer对象直接引用并操作;
2.这块内存不受java堆大小限制,但是受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常;
3.避免了Java堆和native堆中来回赋值的数据,能提高效率。

总结

JVM运行时数据区分区线程私有和线程共享两部分。
线程私有分为程序计数器、本地方法栈、虚拟机栈,程序私有区域会随着线程的产生而产生、结束而结束,故不用考虑过多的内存回收问题。
线程共享区域分为堆和方法区。