likes
comments
collection
share

JVM之运行时方法区

作者站长头像
站长
· 阅读数 43

本文源自Recently祝祝,创自Recently祝祝。转载请标注出处。

此解决方式在企业中有所应用,适合Java初级开发学习,参考。

本文字数与行数,耐心阅读必有收获。

JVM之运行时方法区

1.运行时方法区说明

JVM之运行时方法区

JVM中运行时方法区是Java运行环境中的内存区域,运行时方法区包括了堆、虚拟机栈、本地方法栈、方法区(永久代、元空间)、运行时常量池(字符串常量池)、直接内存。

其中这部分内存可以再次划分为线程共享的内存区域、线程独享的内存区域。其中线程共享内存不会随着线程的销毁而释放。

  • 线程共享:堆,方法区
  • 线程独享:虚拟机栈、本地方法栈、程序计数器。

还可以划分成虚拟机内部内存和虚拟机外部内存。

  • 虚拟机内部内存:虚拟机直接管理的内存
  • 虚拟机外部内存:操作系统管理的内存

2.堆、虚拟机栈、本地方法栈、方法区(永久代、元空间)、运行时常量池(字符串常量池)、直接内存

2.1堆

JVM之运行时方法区

  • JVM中内存最大的区域,存放对象、对象的引用、数组等数据。

  • 堆的结构主要为年轻代(Young)、老年代(Old)、元空间(Meta Spase)

  • 年轻代(Young)又划分成Eden区域、Survivor1区和Survivor0区(简称S0、S1)。

  • 年轻代保存的是新建的对象,新对象在Eden区域被创建,年轻的对象(存活分时间短的对象),老年代保存的是经历过多次垃圾回收任然存活的对象,元空间存储的是元数据:类、方法、字段等数据信息。

  • 两个Survivor区域可以减少垃圾回收的频率和时间,对存活时间短的放在年轻代,标记的是存活的对象而不是被垃圾回收的对象,可以减少垃圾回收的频次,因为存活的对象一般占少数。两部分交替使用,进行垃圾回收之后,两区域留下的存活对象年龄上加1,相互之间复制,减少了垃圾回收的时间,存在Survivor区域的对象比较也比在Eden区域的少,所以需要扫描的对象也减少了,时间就变快了。

  • 堆也是需要进行垃圾回收的主要区域。

  • 堆内存可以通过--xms设置堆初识大小,--xmx设置堆最大大小。元空间的大小取决于硬件,元空间存储在本地,跟虚拟机无关

  • 堆内存是线程共享的区域,对象的改变其他线程也能看见

2.2 虚拟机栈

JVM之运行时方法区

  • 虚拟机栈:在虚拟机中给java方法提供服务的区域。执行java方法的一块内存区域

  • 方法运行的时候运行的时候运行的是栈帧而不是方法本身,栈帧的存活时间也就是方法的入栈出栈的一个周期,一个方法的调用完整的过程也是出栈入账的周期时间内。所以当运行一个开始运行一个方法的时候,栈帧就创建了;方法返回的时候,栈帧就被丢弃了。执行那个方法当前的栈帧就是那个方法的栈帧,当前类也就是当前运行的类。

  • 栈的内存结构:先入后出。栈帧使用支持虚拟机进行方法执行的数据结构。栈帧是虚拟机栈的一个基本单元。

  • 栈帧存储了局部变量、返回地址、操作栈等信息。

  • 栈是存在深度的,如果超过栈的最大深度就会抛出异常,栈溢出。线程过大的时候,内存不够也会抛出异常,内存溢出。

  • 虚拟机栈也是线程独享的区域。

2.3本地方法栈

JVM之运行时方法区

  • 执行本地方法的一块内存区域。

  • 本地方法栈:在虚拟机中给应用到native方法(也就是java语言以外的语言编写的方法)时提供服务的区域。并不是所有时候都是使用java类编写的方法的,有时候还需要使用原生的方法,也就是native方法。本地方就是跟操作系统跟硬件有关。本地方栈就为本地服务

  • 本地方法栈存储了调用本地方的时候所需要的参数、返回值、本地方状态。本地方栈也是一块内存区域,只为本地服务。

  • 也可以使用参数来设置-Xss本地方栈的大小,若超过栈内存大小,也会抛出栈溢出的异常

  • 本地方法栈是线程独享的区域

2.4方法区(永久代、元空间)

JVM之运行时方法区

  • 存储java类编译后的代码的地方,此时的代码已经变成了机器码(本地代码)。也是存储运行时常量池的地方。
  • 方法区在1.8jdk之后永久代就修改成了元空间,元空间的使用让方法区更灵活,性能稳定性更强。
  • 永久代跟元空间的存储位置不一致,永久代存储在堆中,大小也受JVM大小所限制;元数据存储在物理内存区域,也就是本地内存,大小受硬件、物理内存大小的限制。
  • 元空间跟永久代存储数据不一致,元空间存储的元数据例如类、方法等相关信息。
  • 字符串存在永久代中,字符串的数据不断增多,永久代的内存大小就有可能被占满,永久代的大小较难指定,并且永久代中的回收效率低,永久代的垃圾回收跟堆垃圾回收不同,就会有内存溢出的可能。元数据采用的是本地内存,动态的调节内存大小,可以减少了垃圾回收的次数,提高GC效率。

2.5运行时常量池(字符串常量池)

  • 字符串常量池保存的所有带有引号的字符串常量。字符串常量池存放的也是对象地址,而不是具体数据。

    • 保存在堆中,避免了字符串的重复,加快存取效率,方便调试和分析。
    • 全局都只有一个字符串常量池。
    • 字符串本质上就是一个字符数组。编译器的字符串都将存储在字符串常量池中。
    • 存储不变的内存空间。
    • 字符串常量池的存取是基于哈希表实现的,是StringTable数据结构存储的。加快了查询速度,结构由数组+链表的形式实现,当出现哈希冲突的时候,值的形式就会变成链表。变成链表之后查询速度会变慢,但是增删的速度会增快。
    • 字符串常量池中查找数据,返回的是引用,而不是值本身。如果查找的对象不存在,则会创建一个,通过调用intern()方法进行创建对象,并且添加到字符串常量池中,并且返回引用(对象地址)。所以在比较的时候,比较的也是引用的比较,而不是值本身的比较。返回引用也保证了字符串的不可变性。

2.6直接内存

  • 直接内存作用在虚拟机之外的一部分内存区域,非堆内存。直接内存在多次读写操作的情况下,比堆内存要快,毕竟不需要再次做转换,虽然转换时间很快从非直接内存转换本地io的过程,但是频繁的读取的话时间就会显得尤为明显了。但是直接内存申请空间需要消耗更高的性能,频繁申请的时候也会显示很明显。

  • 高频次io更适用于直接内存,不用经过复杂的GC过程。并且生命周期也比堆内存要长。

  • 内存地址受到本级内存地址的限制。

文章全为个人理解,如果发现部分跟你所知道的有出入,欢迎在评论区指出,欢迎探讨。

转载自:https://juejin.cn/post/7225856176131358779
评论
请登录