java虚拟机学习 基于《深入理解 Java 虚拟机》和CYC博客

运行时数据区域

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。
有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。

图片来自CyC博客

线程私有

程序计数器

字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
存放内容:如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地方法,这个计数器值则应为空。

Java虚拟机栈
执行Java代码
Java虚拟机栈也是线程私有的,它的生命周期与线程相同。
存放内容:(私有的)存储局部变量表(基本数据类型和对象引用)、操作数栈(操作指令)、帧数据等信息。用于保存线程的运行状态。
局部变量表中的存储空间以局部变量槽来表示,在编译期间完成分配。分配的“大小”是指变量槽的数量,并非实际内存空间。
异常:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。
操作:可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小
java -Xss2M HackTheJava

本地方法栈
调用操作系统
虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
异常:与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常

线程共有

Java堆是被所有线程共享的一块内存区域,在运行时动态分配内存。所有对象都在这里分配内存,是垃圾收集的主要区域(“GC 堆”)。
存放内容:存放对象实例。
现代垃圾收集器大部分都是基于分代收集理论设计的,“新生代”“老年代”。HotSpot里面也出现了不采用分代设计的新垃圾收集器。
内存空间:Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
异常:如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。
操作:可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。java -Xms1M -Xmx2M HackTheJava

方法区
class文件在内存的存放位置
与Java堆一样,是各个线程共享的内存区域。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
存放内容:存储class文件即类的信息(类名,方法,常量池等)、常量、静态变量、编译后的代码。
异常:HotSpot 虚拟机把它当成永久代来进行垃圾回收。但很难确定永久代的大小,所以经常会抛出 OutOfMemoryError 异常。
方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式。在 JDK 1.8 之后,原来永久代的数据被分到了堆和元空间中。元空间存储类的元信息,静态变量和常量池等放入堆中。

运行时常量池

是方法区的一部分,运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,运行期间也可以将新的常量放入池中。
存放内容:用于存放编译期生成的各种字面量与符号引用
异常:当常量池无法再申请到内存时会抛出OutOfMemoryError异常

直接内存

并不是虚拟机运行时数据区的一部分。

常量:值一旦确定就无法修改
常量池:常量共享的方式,有class文件中的常量池和运行时常量池
class文件中的常量池:编译期生成的东东。包括字面量(字面量就是没有用标识符封装起来的量,是“值”的原始状态,如1,“aa"等)和符号引用量(用符号表示引用对象,方法名,类名,在等号左边的)
运行时常量池:class常量池的东西在类加载后进入运行时常量池(即进入运行状态后),也可放入新常量,如String类的intern()方法。
当调用 intern 方法时,如果字符串池已经包含一个等于此 String 对象的字符串,(由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。
intern之后保证字符串池有这个值。
对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true
采用new 创建的字符串对象,含有变量的值不在字符串池,而是在堆中。

String c = "ab";
String a = new String("ab");
String b = new String("ab");
b.intern() == c; // true
b.intern() == a; // false
String e = "b";
String f = "a" + e;
b.intern() == f;//false,字面值与字符串变量相连,新字符串在堆中新建string变量保存
//字面值与字面值或常量(final修饰)相连则还是常量,保存在常量池中,String s=”a”;s是变量
b.intern() == a.intern();//true
发布了45 篇原创文章 · 获赞 4 · 访问量 1052

猜你喜欢

转载自blog.csdn.net/weixin_43838915/article/details/104725238
今日推荐