JVM1 内存模型/对象访问定位

本文介绍了JVM的内存模型,包括程序计数器、虚拟机栈、本地方法栈、Java堆和方法区,重点讨论了对象创建过程、内存分配方式以及对象的访问定位。此外,还探讨了内存溢出异常,尤其是Java堆和虚拟机栈可能出现的溢出情况。
摘要由CSDN通过智能技术生成

前言:
介绍JVM的内存模型和内存溢出异常

.java .class(字节码文件)

  1. javac : 编译指令
  2. Javadoc 指令:它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。也就是说,只要在编写程序时以一套特定的标签作注释,在程序编写完成后,通过Javadoc就可以同时形成程序的开发文档了。
    javadoc命令是用来生成自己API文档的,类或者方法上通过标记进行解释如:@Author。javadoc 会抽取这些标记,形成和源代码配套的API帮助文档。
    使用方式:使用命令行在目标文件所在目录输入javadoc +文件名.java。
  3. javap :反汇编指令,可以进行反汇编查看编译之后的字节码文件
  4. jar :java archive java 归档,包含了大量的Java类文件和相关数据文件

Java虚拟机向多语言虚拟机发展,可以在JVM上面运行的语言越来越多,并不只是Java

Java8 中添加了Lambda 更加适合于函数式编程(函数式编程更加适合于并行)


一:JVM 的内存模型

JVM在内存中有一块自己的管理区域,执行时会将这块区域划分为不同的数据区,有些区域随JVM进程的启动而存在,有些区域则依赖用户线程的启动/结束而创建/销毁

  1. 程序计数器(Program counter Register): 线程私有,相互独立,指向下一条将要执行的字节码
  2. 虚拟机栈(VM stock) : 线程私有,与线程的生命周期相同。描述Java方法执行的内存模型:每个方法执行时会创建一个栈帧:用于存储局部变量表,操作数栈,动态链接。方法的调用执行到结束就是一个栈帧入栈到出栈的过程
    局部变量表存储了编译时期可知的基本数据类型
  3. 本地方法栈(Native Method stock):与虚拟机栈作用相似,VMStock 中是虚拟机执行的java 方法,Native Method Stock 是虚拟机执行的Native 方法。
  4. Java 堆(java Heap):虚拟机启动时创建,所有的对象实例,数组都要在对上面进行分配,也是垃圾收集管理的主要区域,GC堆(Garbage Collection Heap),要求在内存中不一定物理连续,但是一定要逻辑连续
  5. 方法区(Method Area):存储被加载的类信息,常量,静态变量,即时编译器编译后的代码
    这个区域的内存回收主要针对常量池的回收会对类型的卸载。

JVM的内存模型:
在这里插入图片描述
方法区中有一个重要的部分: 运行时常量池
JDK1.7 以前字符串常量池在Pergem区中(永久带),大小固定,不能被垃圾回收,大量使用字符串native 方法intern 会造成(JDK1.6 以前常量池也在永久带中)
JDK1.7 以后虚拟机去永久化发展
JDK1.7 之后字符串常量区就在堆中,可以被垃圾回收

对象创建

虚拟机意见new 指令时,会先检查是否能在常量池中定义到一个类的符号引用,检查这个符号引用是否被加载,如果没有加载,要先加载类。
为对象实例分配内存空间,类加载之后就可以确定对象所需的内存空间

内存分配的方式

如果java 堆内存是绝对规整的,那么所有用过的内存放在一边,没有用过的放在一边,中间用一个指针作为分界指示器,分配内存就只是将指针向空闲的区域移动对象大小的空间,这叫 “指针碰壁”
如果Java堆内存不是规整的,那么虚拟机要维护一个列表,记录Java堆的内存使用情况,当为对象实例分配内存的时候,从列表中找一块适合的空间分配给对象,这叫 “空闲列表”
Java堆空间是否规整取决于垃圾收集器是否带有压缩整理功能
内存分时还要考虑,分配是否安全,以防出现,为A分配空间,指针还没来得及修改,B就使用了那片空间。解决方案,一是: 保证更新方式对的原子性;二是: 把内存的分配动作按照线程划分在不同的空间中进行,每个线程预先在Java堆中分配一个内存(TLBA),每个线程在自己的空间中完成对象的内存分配,只有当TLBA 用完并分配新的TLBA时,保证操作的原子性
内存分配之后,内存要将分配的内存空间进行初始化,如果使用TLBA,这个擦欧总可以提前到TLBA进行内存分配时执行,对象中的字段,进行对应类型的零值初始化。
在虚拟机的角度,对象的创建已经完成,但是在程序员的角度对象的创建才开始,new 指令之后,执行 方法,按照程序员的意愿对实例进行初始化。

对象的内存布局

对象自己的存储布局:对象头,实例数据,对齐填空

  1. 对象头包含两部分:一是对象自己的运行时数据(哈希表,GC年龄带,锁状态标志,线程持有的锁…) 对象头中的数据是与对象自身定义的数据无关的数据。
    另一部分信息是类型指针,JVM 通过这个来确定这个对象是那个类的实例
  2. 实例数据才是才是对象存储的真正有意义的数据,也是程序中定义的各种字段,包括自己的,从父类继承的。

JVM通过对象头的信息得到对象的大小。

对象的访问定位

JVM通过虚拟机栈中的reference 对对象进行访问。reference 只规定了一个引用但是并没有规定JVM如何通过这个引用去访问Java堆中的对象,

  1. 带有句柄池,直接指针。reference 存储对象句柄的地址。
    在这里插入图片描述
  2. reference直接存储Java堆中对象的地址
    在这里插入图片描述
    reference 直接指向对象实例数据,当的对象移动时(这在堆中十分常见)就需要修改reference 的值,但是这样的访问速度快
    使用句柄池,对象移动之后就只需要修改句柄池中指向对象实例数据的指针

二:内存溢出异常:OutOfMenoryError

介绍内存溢出的另一个原因是为了,高清楚一些数据到底保存在JVM的那块区域

  1. Java堆溢出
    堆中的对象太多,并且JVM没有进行垃圾回收就会出现溢出
  2. 虚拟栈和本地方法栈的溢出
    1. 线程请求的深度大于JVM所允许的最大深度(StackOverFlower),操作系统为每个线程分配的内存空间是有限制的,当该线程请求的栈内存大于这个限制就会抛出StackOverFlower
    2. 当有多个线程,虚拟机扩展栈时无法申请到空间(OutOfMenoryError)
    这虽是两种情况,但本质上是描述的一个问题。在单线程的情况下只会出现第一种情况的异常
  3. 方法区溢出:方法去存放CLAss相关信息,类名,访问修饰符,运行时常量池,字段描述,方法描述等,大量的运行时产生的类填满这个区域就会产生溢出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值