jvm虚拟机内存区域划分(运行时数据区)

大三下学期,距离放假还有一个星期,打算重温一下《深入理解jvm虚拟机》这本书

JVM运行时数据区

我们知道,每个线程都是一个顺序执行的单元

所谓的线程独占区,就是每开辟一个线程,线程内都会包含一个相互独立的(虚拟机栈、本地方法栈、程序计数器),

而所谓的线程共享区,就是多个线程同时共享同一个(方法区、Java堆)。

下面就一起来看看上图所述的JVM运行时的数据区结构

 

先来说说最简单的部分——程序计数器:

1、程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。

2、程序计数器存在于线程独占区,JVM的多线程是通过线程轮流切换并分配处理器来实现的,对于我们来说的并行事实上一个处理器也只会执行一条线程中的指令。为了保证各线程指令的安全顺利执行,每条线程都有独立的私有的程序计数器。

3、如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是native方法,这个程序计数器的值为undefined。

4、此区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域(因为程序计数器是jvm负责维护的,开发人员不进行操作,所以也不存在OutOfMemoryError情况)。

这里多说一句:goto是Java中保留的关键字(就是目前Java不用,也不让你用,为什么要保留呢?可能以后的版本中,goto会被Java使用,若在此之前,Java没用限制你用goto关键字,在日后goto被Java使用时,那么你原先的代码改动就可能比较大了,所以目前来说goto是Java中保留的关键字),若日后可以使用"goto"关键字,那么可能就意味着可以对程序计数器进行操作了。。。

 

 

Java虚拟机栈:

虚拟机栈描述的是Java方法执行的动态内存模型

栈帧:

    每个方法执行,都会开辟一段内存区域用于存放方法执行所需数据——创建一个栈帧,它会伴随着方法从创建到执行完成。栈帧存储着局部变量表、操作数栈、动态链接、方法出口等。  JVM虚拟机它是基于栈的,所以每个方法从调用到执行结束,就对应着一个栈帧在虚拟机栈中入栈和出栈的整个过程。

局部变量表: 编译期可知的各种基本数据类型、引用类型和指向一条字节码指令的returnAddress类型。 

注意!!!局部变量表所需的内存空间在编译期间完成分配。意味着当进入一个方法时,这个方法需要在栈帧中分配多少内存是固定的,在方法运行的期间局部变量表的大小不会发生改变。            

 

思考:根据上面所说的,局部变量表包括了引用类型,如果是一个User对象,user对象中有一个String类型的name属性,那么我们在运行期间会给他设定值,他的值的长度是不固定的,那么他的内存大小会发生改变吗?之前不是说:"在方法运行的期间局部变量表的大小不会发生改变的吗?"

 

答:其实,在局部变量表中所存放的其实只是对象的引用,对象的创建实际是创建在了堆内存中,栈中的局部变量表只是存储了user对象的引用(关于对象的创建过程,后面会提到),这个对象的引用,大小是不会改变的。

栈帧是有大小的,如果栈中所含的栈帧过多,就会出现熟知的StackOverFlowError(可以通过递归进行模拟)

package cn.itcats.jvm.test1;
/**
 * 通过递归循环调用,模拟栈溢出
 * @author itcats
 */
public class StackOverFlowErrorTest {
	public void test() {
		System.out.println("方法执行了");
		test();
	}
	
	public static void main(String[] args) {
		 new StackOverFlowErrorTest().test();
	}
}

抛出大量异常信息

at cn.itcats.jvm.test1.StackOverFlowErrorTest.test(StackOverFlowErrorTest.java:9)
	at cn.itcats.jvm.test1.StackOverFlowErrorTest.test(StackOverFlowErrorTest.java:9)
	at cn.itcats.jvm.test1.StackOverFlowErrorTest.test(StackOverFlowErrorTest.java:9)
	at cn.itcats.jvm.test1.StackOverFlowErrorTest.test(StackOverFlowErrorTest.java:9)
	at cn.itcats.jvm.test1.StackOverFlowErrorTest.test(StackOverFlowErrorTest.java:9)......忽略

 

总结:

1、如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。 

2、如果在动态扩展内存的时候无法申请到足够的内存,就会抛出OutOfMemoryError异常。

 

 

本地方法栈:

(其实HotspotVM不区分Java虚拟机栈和本地方法栈,两者非常相似,但学习不局限与HotspotVM,而是遵循jvm的一个规范,还是有必要学习本地方法栈的)

虚拟机栈:为虚拟机执行Java方法服务。

本地方法栈:为JVM所调用到的Nativa即本地方法服务。

异常方面:  和虚拟机栈出现的异常很相似。

 

 

堆内存(分为新生代和老年代)

新生代分为:

Eden 伊甸园 (被new完后存在于Eden,被新建的对象,垃圾回收常光顾的地方)

Servivor 存活区 (在Eden未被垃圾回收所收集的对象)   FROM区和TO区

老年代:

Tenured Gen (存活区中未被垃圾回收所收集的对象,地位较高的对象)

 

特点:所有线程共享一块内存区域,在虚拟机开启的时候创建。

1、存储对象实例,更好地分配内存。

2、堆内存是jvm所管理的最大的一块内存区域,意味着堆内存也是垃圾收集器(GC)常光顾的一块区域。

3、划分为新生代,老年代,Eden空间,都是为了更好的执行垃圾回收。

4、存放对象实例,几乎所有的对象实例都在这里进行分配。堆可以处于物理上不连续的内存空间,只要逻辑上是连续的就可以。 

5、在JIT编译器等技术的发展下,所有对象都在堆上进行分配。有些对象实例也可以分配在栈中。

6、实现堆可以是固定大小的,也可以通过设置配置文件设置该为可扩展的。  如常见的处理 -Xmx -Xms

7、如果堆上没有内存进行分配,并无法进行扩展时,将会抛出OutOfMemoryError异常。(可以通过死循环内部不断对对象进行创建)

8、类中的成员变量存储在堆区

 

 

 

方法区

特点:所有线程共享一块内存区域。

1、存储虚拟机加载后的类信息,常量,静态变量,即时编译器编译后的代码等数据

(类信息包括类的版本、字段、方法、接口)。

2、HotSpot VM使用永久代来表示方法区,目的就是为了垃圾收集器可以像管理Java堆一样管理这部分内存 ,能够省去了专门为方法区编写内存管理代码的工作,其实这两者并不等价,上述说法仅对于HotSpot。

3、当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

4、运行时常量池存在于方法区中(StringTable  实际是HashSet)

5、方法区又叫静态存储区,存放class文件和静态变量

6、方法区有个别名,叫非堆,有些人喜欢叫他永久代,因为方法区是永久代实现的,但是两者并不等价,仅仅是因为SpotHot使用永久代实现方法区。实现的目的:HotSpot的垃圾收集器可以像管理java堆一样管理这部分内存。圾收集行为在这个区域是比较少出现的,但并非数据进入方法区就如永久代的名字一样永久的存在了。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载。

具体可以了解《JVM中常量池存放在哪里》

package cn.itcats.jvm.test1;

public class StringTableTest {
	/*
	 * main方法执行
	 * 1、Java虚拟机栈创建一个栈帧,栈帧中存储着局部变量表等。
	 * 2、局部变量表: 编译期可知的各种基本数据类型、引用类型和指向一条字节码指令的returnAddress类型。
	 * 3、因为局部变量表所需的内存空间在编译期间完成分配,字符串长度不定,所以栈帧中实际存放的是引用a与b
	 * 4、若在Java堆中创建了两个对象"abc",则结果应该为false(地址不同)
	 * 5、事实上,abc存放在方法区中的常量池(StringTable)中,实际上是HashSet,引用a的"abc"先存放入StringTable中,
	 *    因为Set是无序不重复的,可知StringTable中目前只存在一个abc,
	 *    而Java虚拟机栈中栈帧中的局部变量表的引用a和b同时指向方法区中的常量池的同一"abc"
	 */
	public static void main(String[] args) {
		String s1 = "abc"; 		//存在于常量池,字节码常量
		String s2 = "abc";		//存在于常量池,字节码常量
		System.out.println(s1 == s2);  		//true
		String s3 = new String("abc");
		System.out.println(s1 == s3);  		//false  通过new创建对象一定存在于堆内存
		System.out.println(s1 == s3.intern());   //true   运行时常量池,将堆内存中的s3迁移到方法区的常量池中,比较则是用常量池的"abc"相互比较
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM内存模型是Java虚拟机运行时所使用的内存分配和管理方式。它包括了运行时数据,也就是JVM内存划分的不同区域,用来存储程序的数据和指令。 JVM运行时数据主要包括以下几个部分: 1. 堆(Heap):用于存储对象实例和数组。堆是线程共享的区域,所有线程共同使用堆来创建和访问对象。 2. 方法(Method Area):用于存储已加载的类信息、常量、静态变量和编译后的代码等。方法也是线程共享的区域,它在内存用一块连续的空间。 3. 虚拟机栈(VM Stack):每个线程在创建时都会分配一个虚拟机栈,用来存储局部变量和方法调用信息。虚拟机栈是线程私有的,每个线程都有自己独立的虚拟机栈。 4. 本地方法栈(Native Method Stack):与虚拟机栈类似,用于存储本地方法调用的相关信息。 5. 程序计数器(Program Counter Register):用于存储当前线程执行的字节码指令的地址。 这些不同的运行时数据JVM内存模型中起着不同的作用,可以提供给程序运行所需的各种资源和环境。例如,堆用于存储对象实例,方法用于存储类信息和静态变量,虚拟机栈用于存储方法的局部变量和方法调用信息等。 总的来说,JVM内存模型和运行时数据Java虚拟机运行时所使用的内存管理和分配方式。它们的不同区域有不同的作用,用来存储程序的数据和指令。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [运行时数据JVM内存模型](https://blog.csdn.net/weixin_45659364/article/details/124027073)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [JVM:Java内存模型与运行时数据区域](https://blog.csdn.net/m0_71777195/article/details/131655107)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java内存模型与JVM运行时数据别详解](https://download.csdn.net/download/weixin_38648037/12745990)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值