JVM<整体内存结构>基础二

开篇闲谈

第二篇了~,昨天看到一段北漂的访谈,中间有段谈话感同身受 ,主持问:“为什么不考虑回老家呢?”,访谈者这么回答的:“在北京这种大城市生活很简单,没有那么多人情世故,适者生存,但老家就不一样了。”分享给各位大佬。步入正题,接着整理JVM基础。

栈帧执行对内存区域的影响

实例<方法运行时栈帧整个过程>

//简单代码
public class Test {

    public static void main(String[] args) {
        A();
    }
    public static void A(){
        int a = 2;
        int b = 3;
        System.out.println(a+b);
    }
}

对 class 进行反汇编 命令 javap –c Test.class
在这里插入图片描述
字节码解释地址:链接: https://cloud.tencent.com/developer/article/1333540.

JVM整体内存结构

在这里插入图片描述

本地方法栈

本地方法栈和java虚拟机栈是很相似的一个区域,java虚拟机栈用于管理java函数的调用,而本地方法栈则用于管理本地方法的调用,但是呢,本地方法并不是Java实现的,而是由C语言实现的(比如 hashcode 方法,clone方法),它的服务对象是native方法,其实也可以理解为本地方法栈和java虚拟机栈是同一个区域。

方法区

方法区是各条线程共享的运行时内存区域。它储存每个类的结构信息,比如运行时常量池字段和方法的数据、构造函数和普通方法的字节码内容、还有一些在类、实例、接口初始化时用到的特殊方法。
它也是JVM对内存的“逻辑划分”,在JDK1.7之前习惯性把方法区称为“永久代”,因为在HotSpot虚拟机中,设计人员使用永久代来实现方法区。而在JDK1.8及以后使用了元空间来实现方法区。说白了就是一个名字的问题。

补充JVM发展史
在这里插入图片描述

元空间

它是 方法区JVM 规范的一种实现,本质和永久代类似。
HotSpot 虚拟机、JDK1.7 版本中已经将永久代的静态变量和运行时常量池转移到了堆中,其余部分则存储在 JVM 的非堆内存中,而 JDK1.8版本 已经将方法区中实现的永久代去掉了,并用元空间(class metadata)代替了之前的永久代,不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过参数来指定元空间的大小。

元空间大小参数
jdk1.7 及以前(初始和最大值):-XX:PermSize;-XX:MaxPermSize;
jdk1.8 以后(初始和最大值):-XX:MetaspaceSize; -XX:MaxMetaspaceSize
jdk1.8 以后大小就只受本机总内存的限制(如果不设置参数的话)

JVM 参数参考:链接: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html.

Java8 为什么使用元空间替代永久代,这样做有什么好处呢?
官方给出的解释是:
移除永久代是为了融合 HotSpot JVM 与 JRockit VM 而做出的努力,因为 JRockit 没有永久代,所以不需要配置永久代。 永久代内存经常不够用或发生内存溢出,抛出异常 java.lang.OutOfMemoryError: PermGen。这是因为在 JDK1.7 版本中,指定的 PermGen 区大小为 8M,由于 PermGen 中类的元数据信息在每次 FullGC 的时候都可能被收集,回收率都偏低,成绩很难令人满意;还有为 PermGen 分配多大的空间很难 确定,PermSize 的大小依赖于很多因素,比如,JVM 加载的 class 总数、常量池的大小和方法的大小等。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分,存放从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用(符号引用)。 运行时常量池相对于 Class 常量池的另外一个重要特征是具备动态性。、

堆是JVM上最大的内存区域,我们申请的所有对象,几乎都存储在这里,还有我们常说的垃圾回收,也是操作的堆。
还有堆空间一般来说是程序启动的时候就已经申请了,但是不是全都使用(可以设置可伸缩的)
那问题来了,当对象创建时,到底是在堆上分配,还是在栈上分配?
其实这个和两方面有关:对象的类型和在java类中的位置。
java对象可以分为基本数据类型和普通对象。
1.对于普通对象来说,JVM首先会在堆上创建对象,然后在其他地方的使用其实是它的引用,比如在虚拟机栈局部变量表中存在这个对象的引用。
2.对于基本数据类型,也分为两种情况,当在方法中声明时,会在栈上直接分配。其他情况都会在堆上。

堆大小参数
-Xms:堆的最小值;
-Xmx:堆的最大值;
-Xmn:新生代的大小;
-XX:NewSize;新生代最小值;
-XX:MaxNewSize:新生代最大值;

直接内存

比较科学的叫法是堆外内存,java虚拟机堆之外的内存,直接受操作系统管理(而不是虚拟机)。
JVM在运行时,会从操作系统申请大块的堆内存,进行数据存储;同时还有虚拟机栈、本地方法栈和程序计数器,这块称为栈区,操作系统剩余的内存为堆外内存(直接内存)。

它不是虚拟机运行时数据区的一部分,也不是 java 虚拟机规范中定义的内存区域;如果使用了 NIO,这块区域会被频繁使用,在 java 堆内可以用 directByteBuffer 对象直接引用并操作;

这块内存不受 java 堆大小限制,但受本机总内存的限制,可以通过-XX:MaxDirectMemorySize 来设置(默认与堆内存最大值一样),所以也会出现 OOM 异常。

后记

各位大佬多多指正,下章见~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值