2.内存模型

JVM内存共分为五块区域
在这里插入图片描述

程序计算器

程序计数器是线程私有的,指的是JVM运行时指的下一条字节码的行号,跟CPU当中的程序计数器不一样,这里的程序计数器是JVM指令级别的,而CPU内部的程序计数器是位于CPU的一个物理空间,通常我们把它称作——寄存器,它指向的是下一条CPU指令的内存地址,它是CPU指令级别的。
特点
1、线程私有
2、占用内存很小
3、在内存当中不可能出现OOM(OutOfMemory),所有对一这块区域也不存在垃圾回收这一说法
4、如果是在执行native方法,则是未指定值(undefined),因为程序计数器不负责本地方法栈

方法区

所有线程共享的一块内存区域。
方法区主要有:常量,静态变量,类元信息(类信息),永久代是它的一种实现。
Class文件:除了类的字段,方法,接口等描述信息外,还有一个常量池,存放编译期生成的各种字面量和符号引用。
字面量:比如String类型的字符串或final类型的常量的值。
符号引用:类或接口的全限定名,变量或方法的名称,变量或方法的描述信息,this
方法区中有一个运行时常量池,class文件加载到内存后,class文件常量池的内容转移到运行时常量池。并非Class文件常量池的内容才能进入运行时常量池,运行期间也可将新的常量池放入运行时常量池。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。
在这里插入图片描述
Java8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。

虚拟机栈

虚拟机栈也是线程私有的,每个线程独有一块栈。一个栈由多个栈帧组成,每个栈帧对应着一个被调用的方法,例如main线程有main方法,又调用了compute()方法。main线程的栈里就会产生两个栈帧,栈帧是方法独有的,不存在栈帧的嵌套,a方法调用了b方法,则栈里产生两个独立的栈帧,而非嵌套关系。栈帧有局部变量、引用、操作数栈、动态链接、方法的出口等信息。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
在这里插入图片描述
在这里插入图片描述

public int compute() {
    int a = 1;
    int b = 2;
    int c = (a + b) * 10;
    return c;
}

运行这个compute()方法:
0: iconst_1 将int类型常量1压入栈,即把1操作栈里
1: istore_1 将int类型值存入局部变量1(局部变量表中给a分配一块内存,1出栈,a=1)
2: iconst_2 将int类型常量2压入栈,即把2放操作栈里
3: istore_2 将int类型值存入局部变量表2(局部变量表中给b分配一块内存,2出栈,b=2)
4: iload_1 从局部变量1中装载int类型值(把1装载到操作数栈)
5: iload_2 从局部变量2中装载int类型值(把2装载到操作数栈)
6: iadd 加法,即从操作栈里把操作数拿出来,出栈做加法,并把加法后的值(3)压入栈
7: bipush 10 把10压入栈
9: imul 乘法,即把操作数出栈,做乘法,把值(30)压入栈
10: istore_3 将int类型值存入局部变量3(局部变量表中给c分配一块内存,30出栈,c=30)
11: iload_3 从局部变量3中装载int类中值(把30装载到操作数栈)
12: ireturn 从方法中返回int类型数据
操作数栈:
操作数,即变量的值,在做运算的时候,需要内存空间进行存放,而操作数栈就是放操作数的地方。
就是操作数在程序运行期间要做操作的一块临时存放的内存空间。
方法出口:
math.compute();执行完要回到main方法继续执行。在调用math.compute();时已经把main的运行现场(要回到哪个位置,哪行代码)放到了方法出口里,根据方法出口的信息知道回到main后怎么走。
局部变量表:
Math math = new Math();
math放堆里,而main线程的栈帧(具体来说是栈帧中的的局部变量表里)也有一个math,这个math放的是在堆里的内存地址。

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。本地方法是c语言实现的。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError 和OutOfMemoryError异常。

堆是被所有线程共享的一块区域,是 JVM 所管理的最大的一块内存空间,在虚拟机创建时启动。堆当中主要用于存放各种类的实例对象,在程序执行过程中使用new运算符创建的Java对象,存储在堆内存当中。对象内部有实例变量,所以实例变量存储在堆内存当中。是垃圾回收机制主要管理的区域。
堆的划分:
在这里插入图片描述

扩展 gc

jvm虚拟机调优的目的就是减少gc,主要是full gc,因为full gc时间长,对应STW也较长。
为什么设计STW?
因为gc的过程,是在找非垃圾对象,比如从math对象出发,找到的所有引用对象都是非垃圾,然后gc还没结束,一个用户线程结束了,栈空间释放,math对象也被释放,之前找的这些对象都变成了垃圾对象,之前的工作就白做了。
所以设计了STW,gc的时候让用户线程停止。
对于不同的垃圾回收器,一般gc过程不同,大体过程一样。

扩展 JVM内存参数

在这里插入图片描述
在HotSpot中,Eden空间和另外两个survivor空间缺省所占的比例是8 : 1 : 1,当然开发人员可以通过选项
-XX:SurvivorRatio调整这个空间比例。比如-XX:SurvivorRatio=8
几乎所有的Java对象都是在Eden区被new出来的。绝大部分的Java对象的销毁都在新生代进行了(有些大的对象在Eden区无法存储时候,将直接进入老年代)
IBM公司的专门研究表明,新生代中80%的对象都是“朝生夕死”的。
可以使用选项"-Xmn"设置新生代最大内存大小,但这个参数一般使用默认值就可以了。
新生区的对象默认生命周期超过 15 ,就会去养老区养老。-XX:MaxTenuringThreshold=N

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值