当前位置: 首页 > news >正文

JVM:堆、方法区

一、堆

  1. 概念:堆用于存储对象和数组,主要分为新生代和老年代,新生代又细分为伊甸园区、幸存者 0 区(S0)和幸存者 1 区(S1)
  2. 内存设置:可用 -Xmx 和 -Xms 设置堆内存大小,-Xmx 为堆内存最大值,-Xms 是初始大小。若不设置,默认初始大小为物理内存的 1/64,最大为 1/4。超出最大内存,JVM 抛出内存溢出异常
  3. 新生代与老年代:新对象先存于伊甸园区,GC 后存活对象移至幸存者区,在 S0 和 S1 间移动。多次 GC 后仍存活的对象进入老年代。默认新生代与老年代大小比例为 1:2,可通过 -XX:NewRatio 调整。新生代中,伊甸园区和两个幸存者区默认比例为 8:1:1,可通过 -XX:SurvivorRatio 调整
  4. 对象分配:新对象(非大对象)先入伊甸园区,满后触发 Minor GC。首次 GC 时,伊甸园区存活对象移至 S0 或 S1 区,对象年龄加 1。后续每次 GC,存活对象在 S0 和 S1 间切换,默认对象年龄达 15 进入老年代。老年代满触发 Major GC(Full GC),若仍无法分配内存则抛出异常
  5. GC 分类:Minor GC 回收新生代;Major GC 主要回收老年代,实际中常伴随 Full GC;Full GC 会回收整个堆,包括新生代和老年代
  6. 分代目的:分代是为优化 GC 效率,有针对性回收,避免全堆回收耗时过长。多数对象在伊甸园区分配,大对象直接进入老年代
  7. TLAB:JVM 为每个线程在伊甸园区分配 TLAB,避免多线程对象分配时的竞争问题,保证线程安全。若 TLAB 空间不足,线程会加锁获取伊甸园区空闲区域以扩容

二、方法区

  1. 方法区是所有线程共享的。自 Java 8 开始,元空间成为其一种实现,它使用本地内存,不再受 JVM 堆内存限制,但仍受 JVM 管理
  2. Java 8 及以后,可通过 -XX:MetaspaceSize(初始默认大小)和 -XX:MaxMetaspaceSize(最大大小)设置其大小,超出最大大小会抛出内存溢出异常
  3. 内部结构:包含类信息(类的定义,如属性、方法定义及方法字节码,还有类的继承关系)、静态变量、运行时常量池(存放类中的常量、符号引用等)、JIT 即时编译器编译后的代码缓存
  4. GC 时方法区垃圾会被回收。常量无引用时会被回收;类需同时满足三个条件才会被回收:该类所有实例被销毁该类的 java.lang.Class 对象未被引用加载该类的类加载器已被回收

三、其它相关知识

(1)TLAB(Thread Local Allocation Buffer)

  1. 堆内存对象分配的线程安全问题在多线程环境下,如果多个线程同时在堆的 Eden 区进行对象分配,可能会出现线程安全问题。因为堆是线程共享的内存区域,当多个线程同时尝试修改 Eden 区的空闲内存指针时,就可能导致数据不一致。例如,线程 A 和线程 B 同时读取到空闲内存指针指向地址 X,线程 A 在该地址创建了一个对象,然后将空闲内存指针向后移动;但线程 B 并不知道指针已经移动,仍然在地址 X 处创建对象,这就会造成内存覆盖和数据混乱
  2. TLAB 避免线程安全问题的原理
    1. 线程私有缓存区域:TLAB 是在 Eden 区内为每个线程单独分配的一块私有缓存区域。每个线程在自己的 TLAB 中进行对象分配,就好像每个线程都有自己的 “专属领地”。由于 TLAB 是线程私有的,一个线程对其 TLAB 内的内存操作不会影响其他线程的 TLAB,因此避免了多个线程同时访问和修改同一内存区域的情况,从根本上解决了线程安全问题
    2. 减少锁竞争:如果没有 TLAB,线程在堆上分配对象时可能需要加锁来保证线程安全,频繁的加锁和解锁操作会带来较大的性能开销。而使用 TLAB 后,线程在自己的 TLAB 内分配对象无需加锁,只有当 TLAB 空间不足需要重新分配 TLAB 时,才需要同步锁定 Eden 区以获取新的 TLAB 空间,大大减少了锁竞争,提高了对象分配的效率
  3. 示例说明假设有两个线程 Thread1 和 Thread2 同时运行,JVM 为它们分别分配了 TLAB1 和 TLAB2。当 Thread1 需要创建一个新对象时,它会直接在 TLAB1 中分配内存,不会影响 Thread2 的 TLAB2;同理,Thread2 在 TLAB2 中进行对象分配时也不会受到 Thread1 的干扰。只有当 TLAB1 或 TLAB2 空间不足时,线程才会去申请新的 TLAB 空间,并且在这个过程中会进行同步操作,以确保多个线程不会同时修改 Eden 区的空闲内存指针

(2)逃逸分析

3.2.1逃逸分析的概念

  1. 方法逃逸:一个对象在方法中被创建,但是它的引用被传递出了该方法,可能被其他方法使用。例如,在方法中创建对象并将其作为返回值返回,或者将对象的引用赋值给类的成员变量等
  2. 线程逃逸:一个对象的引用可以被多个线程访问到,比如将对象的引用存储在静态变量或者共享的集合中

3.2.2栈上分配

如果逃逸分析的结果表明一个对象不会发生逃逸,也就是该对象的引用不会超出当前方法的作用域,那么 JVM 可以选择将这个对象分配到栈上,而不是堆上

  1. 原理:栈上分配的对象会随着方法的结束而自动销毁,不需要垃圾回收器进行回收,这样可以减少堆内存的压力,也避免了垃圾回收带来的性能开销
  2. 示例
    public class StackAllocationExample {public static void main(String[] args) {for (int i = 0; i < 1000000; i++) {createObject();}}public static void createObject() {// 这里创建的对象未发生逃逸Point point = new Point(1, 2); }
    }class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}
    }

在上述代码中,createObject 方法里创建的 Point 对象没有发生逃逸,JVM 就可能将其分配到栈上

3.2.3同步省略(锁消除)

当逃逸分析发现一个对象不会发生线程逃逸时,那么对该对象的同步操作(加锁)就可以被消除

  1. 原理:因为对象不会被其他线程访问,所以对它进行同步操作是没有必要的,JVM 会在编译时自动将这些不必要的同步代码去掉,从而减少了同步带来的性能开销
  2. 示例
    public class SyncEliminationDemo {public void test() {// 创建局部对象,不会发生线程逃逸Object lock = new Object();synchronized (lock) {System.out.println("执行同步块");}}
    }

在上述代码中,lock对象是test方法的局部变量,仅在该方法内使用,不会被其他线程访问。JVM 编译时,经逃逸分析确定lock无线程逃逸风险,会消除synchronized同步块,优化为:

public class SyncEliminationDemo {public void test() {Object lock = new Object();System.out.println("执行同步块");}
}

如此,避免了同步操作带来的性能开销,提升了程序执行效率

3.2.4标量替换

如果一个对象不会发生逃逸,JVM 可以不创建这个对象,而是将对象的成员变量分解成一个个独立的标量(基本数据类型)来代替

  1. 原理:将对象拆分成标量后,这些标量可以直接在栈上分配和操作,避免了对象创建和访问的开销,进一步提高了性能
  2. 示例
    public class ScalarReplacementExample {public static void main(String[] args) {alloc();}public static void alloc() {// 这里的 Point 对象可能会被进行标量替换Point point = new Point(1, 2); int x = point.x;int y = point.y;System.out.println(x + y);}
    }class Point {int x;int y;public Point(int x, int y) {this.x = x;this.y = y;}
    }

在 alloc 方法中,Point 对象没有发生逃逸,JVM 可能会将 Point 对象拆分成 x 和 y 两个标量,直接在栈上分配和使用

相关文章:

  • chili3d调试笔记2
  • 文章记单词 | 第34篇(六级)
  • 时序数据预测:TDengine 与机器学习框架的结合(二)
  • 图神经网络入门代码(2)-逐行分析
  • Windows 图形显示驱动开发-WDDM 1.2功能—Windows 8 中的 DirectX 功能改进(二)
  • 7系列fpga在线升级和跳转
  • LINUX 416 路由转发1
  • 计算机视觉与深度学习 | 图像特征点提取算法及匹配算法综述
  • 使用 tcpdump 工具,捕获并分析
  • 照片处理工具:基于HTML与JavaScript实现详解
  • 实验三 I/O地址译码
  • c++原子操作
  • Day09 【基于LSTM实现文本加标点的任务】
  • # 手写数字识别:使用PyTorch构建MNIST分类器
  • AI赋能智能经营:全球关税战下的可持续发展之道
  • 2000-2019年各省城市液化石油气用气人口数据
  • 人工智能概念股投资:10大潜力标的深度研究
  • AutoDL上Xinference安装
  • JVM-基于Hotspot
  • JVM 调优不再难:AI 工具自动生成内存优化方案
  • 邢台移动网站建设服务/seo一键优化
  • 邢台集团网站建设/八百客crm登录入口
  • 文字游戏做的最好的网站/今日要闻新闻
  • 本地人wordpress怎么同步到服务器/济南seo外贸网站建设
  • 怎么介绍vue做的购物网站项目/百度400电话
  • 推荐一些做网站网络公司/2022最新免费的推广引流软件