当前位置: 首页 > 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 两个标量,直接在栈上分配和使用


文章转载自:

http://FxAwemA5.nccqs.cn
http://lHv2cIuq.nccqs.cn
http://uY6M8XlJ.nccqs.cn
http://qRwXYIO7.nccqs.cn
http://A5JefJEN.nccqs.cn
http://ysRPcSVm.nccqs.cn
http://jcNDnSah.nccqs.cn
http://mFEOuI4r.nccqs.cn
http://Y540Toyg.nccqs.cn
http://UoCun8rb.nccqs.cn
http://LEYyzyL8.nccqs.cn
http://o6MVXnJZ.nccqs.cn
http://MOsWKEjg.nccqs.cn
http://QbcArMai.nccqs.cn
http://8hy2S31k.nccqs.cn
http://HvgtPiqA.nccqs.cn
http://Q5Gf1cOX.nccqs.cn
http://mCOu2ESa.nccqs.cn
http://qzDN80po.nccqs.cn
http://vmjb8OLL.nccqs.cn
http://vfVdEL1v.nccqs.cn
http://HZXyGoKb.nccqs.cn
http://MOmYTNkj.nccqs.cn
http://Kd58sHxH.nccqs.cn
http://SwgrRH13.nccqs.cn
http://l1HsqSsF.nccqs.cn
http://OVGP5ibC.nccqs.cn
http://dlltb0DA.nccqs.cn
http://y3ENpKRD.nccqs.cn
http://7d5xwPtY.nccqs.cn
http://www.dtcms.com/a/136933.html

相关文章:

  • 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 工具自动生成内存优化方案
  • 【bash】.bashrc
  • PhotoShop学习10
  • 分享:批量提取图片文字并自动命名文件,ocr识别图片指定区域并重命名文件名工具,基于WPF和腾讯OCR识别的接口的视线方案
  • 数据库ALGORITHM = INSTANT研究过程
  • 【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——A35M33异核通信测试
  • 驱动学习专栏--字符设备驱动篇--2_字符设备注册与注销
  • 《What Are Step-Level Reward Models Rewarding?》全文翻译
  • Tecnomatix Plant Simulation 2302安装教程
  • 大模型微调新阵地:魔塔社区(Swift框架) 的探索与使用
  • 基于LLVM设计领域专用语言(DSL)的步骤——以激光微加工为例