java 虚拟机内存区域

*Java是一套体系,包括Java程序设计语言、class字节文件、Java虚拟机和Java API。程序设计语言就是语法,思想,语义等,class字节文件时java程序编译后形成的可直接在java虚拟机中运算的文件,Java虚拟机是Java程序能够运行的保障,Java API提供了各种各样的功能接口。

Java是跨平台的,因为只要是该平台上有Java虚拟机,那就可以运行Java程序,因为真正运行Java程序的是Java虚拟机。

不仅仅是Java语言可以在Java虚拟机上运行,许多其他的计算机语言也可以,甚至一些传统的语言,也衍生出了能够在jvm上运行的Java虚拟机版本,足以见得Java 虚拟机的重要性。

在64位操作系统上运行,64位的Java虚拟机比32位的虚拟机多消耗10%~30%内存,运行速度几乎也慢了15%左右,随着发展,64位虚拟机是必然主流的。

class文件是二进制数据,只有JVM可以识别,JVM就根据识别到的内容去执行,Java代码是不能访问和操作底层的,所以这个“执行”就是调用本地方法,由本地方法去做。

Java虚拟机运行时管理的内存区域

 

程序计数器(Program Counter Register)  线程私有

前言:Native关键字是修饰方法(函数)的,被native修改的方法叫做本地方法,和平台有关,可移植性不好,类似于abstract抽象一样,只有方法名,没有方法的具体实现,方法的实现通常是能够访问操作系统底层的计算机语言编写的(C或者C++等等),因为Java语言不能够访问操作系统底层,所以Java代码可以调用非Java代码来访问操作系统底层(通过这个native方法)。

程序计数器是以块很小的内存,作用是记录下一条指令的地址,有点像PC寄存器,每个线程都有自己的一个程序计数器。多线程的时候,cpu是轮流切换运行线程,那么程序计数器可以帮助自己的线程执行和切换恢复。如果线程正在执行的是Java代码,那么程序计数器记录下一条指令地址,如果是native代码,那么记录值为空

Java虚拟机对这块内存没有规定任何OutOfMemoryError异常。

Java虚拟机栈  线程私有

生命周期和线程一样,一个线程一个栈,线程的创建就包括栈的创建,线程结束,栈就销毁。线程里的方法就对应于栈中的一个栈帧。出栈入栈的动态过程就和这些程序的一样(当助教的时候,已经详细学过了)。

这个区域规定了两种异常:1,当线程访问的深度超过了栈的深度(栈大小),出界了,报异常StackoverFlowError;2,当栈动态扩展的时候,扩展需要的内存大小没有找到的时候,就会报OutOfMemoryError异常。

虚拟机栈供Java代码用。

本地方法栈

Native方法是本地方法,本地方法是有能够访问操作系统底层的语言写的,那么这个本地方法执行的时候也需要“栈帧”,所以有了本地方法栈。也有StackoverFlowError和OutOfMemoryError。

Java堆是虚拟机管理的内存中最大的,在虚拟机启动时就创建,唯一目的就是存放对象(类的实例),几乎所有的对象都放在堆中(用new 创建),所以堆是垃圾管理的重要针对对象。堆不要求在物理内存上是连续的,可以是间断的,只要逻辑上是连续的就行,所以堆的大小是可以扩展的,如果堆已经没有内存去存放实例了,又无法扩展了,就抛出异常OutOfMemoryError。

方法区

和堆类似,不要求物理内存上连续,可以扩展。用于存放代码相关的信息(比如一个类的类名,访问修饰符,常量池,类型,父类,实现的接口 等等)。也有OutOfMemoryError。

运行时常量池

运行时常量池是方法区的一部分(jdk1.8以后,常量池放在了堆中,是堆的一部分),用于存放常量的。也OutOfMemoryError。 常量池主要存两大类数据:字面量和符号引用。字面量:就是有字母、数字、符号组成的值,只能为右值,int a=8; 8就是字面量,String s=“hello” ,hello就是字面量。符号引用:Java代码中,不就是类、接口、类的字段、类的方法这些吗,给它们一个引用和描述符,存在常量池,通过这个引用,就可以掌握代码的框架,然后找到相应的类-》字段或者方法,执行相应代码。

直接内存

直接内存就是不归虚拟机管理的内存,就是机器的内存中剩余的内存。虚拟机可以使用native方法在直接内存中分配一块内存,然后在堆中用一个对象引用指向这块内存,就可以操作这块内存了。

举个例子---------对象访问

Object o = new Object();  假设这句在一个方法中。

 

1、在方法中,这个方法就会有一个栈帧,栈帧会为变量等初始化空间出来,包括对象的引用。Object o就是一个引用,存放在栈帧中,作为reference类型,这个栈帧肯定是存放在Java虚拟机栈中的。

2、new Object(); 这句是创建了一个实例,在Java堆中分配一个空间,存放着这个对象,不仅包含这个对象的变量的初始化数据等,还包含能够查找到Object类的相关信息的地址(就是方法区中存的内容的地址)。

3、此时,方法区还会存储这个Object类的相关信息。

 

栈帧中的“引用”如何去找到对象数据 和 类的相关数据?主流方式有两种:句柄 和 直接使用指针。

句柄:

Java堆中分配一块内存,用于存储句柄,这块内存叫做句柄池。句柄池存放着对象实例数据和对象类型数据(就是前面讲的“类的相关数据”,以后都叫 对象类型数据 了)的地址,而引用只是句柄的地址,一旦对象的地址变了,JVM只需要修改句柄的内容即可,不需要修改引用。

指针:

对象类型数据,还是通过句柄来,但是对象实例数据,就直接用指针,引用就是对象实例数据的地址,对象的内存地址一旦变了,那么引用也得变。

句柄和直接指针 ,各有优劣。不过这两种方式都非常符合java特性,就是一个类可以有多个实例对象,类信息就一个,存在方法区就行,但是实例对象可以有多个,存在堆中。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值