JVM工作原理与实战(十八):运行时数据区-堆

简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了运行时数据区、堆介绍、堆的关键参数等内容。

一、运行时数据区

Java虚拟机(JVM)在运行Java程序期间,会创建并维护一系列内存区域,这些区域总称为运行时数据区。这些区域根据其用途和特性,被严格定义并管理。《Java虚拟机规范》详细规定了这些区域的作用和行为,以确保所有Java虚拟机实现的一致性和正确性。

线程不共享区域:

  • 程序计数器:用于存储当前线程执行的字节码指令地址。这个区域是每个线程独有的,不共享。
  • Java虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,每个方法调用都会创建一个栈帧,用于存储局部变量、操作数栈、动态链接和方法出口信息。
  • 本地方法栈:与虚拟机栈相似,本地方法栈为native方法提供服务。

线程共享区域:

  • 方法区:用于存储已被JVM加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。
  • :堆是所有线程共享的区域,用于动态分配内存。所有的对象实例以及数组都应当在堆上分配。

image.gif

二、堆

1.堆介绍

在Java虚拟机(JVM)中,堆是用于动态分配内存的区域,也是空间最大的部分。所有通过new关键字创建的对象实例都将在堆上分配内存,这使得堆成为了管理对象生命周期的重要区域。

局部变量表是存放在栈内存中的,它主要存放的是基本数据类型和对象引用,这个对象引用可以指向堆上的对象。静态变量也可以存储堆上对象的引用,这使得静态变量成为线程之间共享对象的一种方式。

image.gif

堆内存大小是有限制的,一旦应用程序持续地将对象分配到堆中,导致其达到上限,就会触发OutOfMemory异常,导致程序运行失败。为了避免这种情况,开发人员需要密切关注堆内存的使用情况,并采取适当的措施来管理内存分配。这包括优化代码以减少内存使用,实施垃圾回收机制,或增加可用内存量等。通过有效的内存管理,可以提高应用程序的稳定性和性能。

模拟堆区的溢出案例

通过使用new关键字,不断地创建对象,并将它们添加到集合中,来模拟堆内存的溢出。随后,观察堆溢出后产生的异常信息。

public class Demo1 {
    public static void main(String[] args) {
        ArrayList<Object> objects = new ArrayList<>();
        while(true){
            objects.add(new byte[1024]);
        }
    }
}
image.gif

运行结果:

image.gif

2.关键参数

在Java堆空间管理中,有以下几个关键参数和概念需要关注:

  • 已使用堆空间(Used Heap Space):这个值表示当前已经被应用程序分配和使用的堆内存量。这个数值可以帮助了解应用程序的内存需求以及是否存在内存泄漏问题。
  • 总堆内存(Total Heap Memory):这是Java虚拟机(JVM)已经分配的可用堆内存量。这个数值可以帮助了解JVM的最大可用内存。
  • 最大堆内存(Max Heap Memory):这是JVM可以分配的最大堆内存量。这个数值可以帮助了解JVM的内存限制,以及是否需要调整JVM的启动参数来增加最大堆内存。

通过arthas查看堆内存:

堆内存used total max三个值可以通过dashboard命令看到,并且可以手动指定刷新频率(不指定默认5秒一次)。

dashboard -i 5000

image.gif

案例:

public class Demo1 {
    public static void main(String[] args) throws IOException {
        System.in.read();
        ArrayList<Object> objects = new ArrayList<>();
        while(true){
            objects.add(new byte[1024]);
        }
    }
}
image.gif

运行结果:

image.gif

在堆内存使用量增加的情况下,当可用内存接近饱和时,Java虚拟机将继续为堆分配内存。如果堆内存耗尽,Java虚拟机将不断进行内存分配,从而使total值相应地增大。需要注意的是,total值不会超过max值(max包括total,total包括used )。

image.gif

在未指定任何虚拟机参数的情况下,max的默认值是系统内存的四分之一,而total的默认值是系统内存的六十四分之一。在实际应用中,可以手动配置total和max的值以确保系统的稳定运行。

修改堆的大小,可以使用虚拟机参数 –Xmx(max最大值)和-Xms (初始的total)。语法为-Xmx值 -Xms值,单位可以为字节(默认,是 1024 的倍数)、k或者K(KB)、m或者M(MB)、g或者G(GB)。需要注意的是Xmx必须大于2MB,Xms必须大于1MB。

案例:

public class Demo1 {
    public static void main(String[] args) throws IOException {
        System.in.read();
        ArrayList<Object> objects = new ArrayList<>();
        while(true){
            objects.add(new byte[1024]);
        }
    }
}
image.gif

设置堆的大小:

-Xmx4G -Xms4G
image.gif

image.gif

运行结果:

image.gif

在Arthas中显示的堆内存大小与设置的值存在差异,这是由于Arthas使用了JMX技术来获取堆内存的信息。JMX技术中的内存获取方式与垃圾回收器相关,它计算的是可分配对象的内存量,而不仅仅是整个内存。


在Java服务端程序的开发过程中,建议将-Xmx和-Xms参数设置为相同的值。这样做可以确保程序启动后可用的总内存即为最大内存,避免了向Java虚拟机再次申请内存的需求。这减少了申请和分配内存所需的时间开销,并避免了因内存过剩而导致的堆内存收缩情况。


-Xmx参数的具体设置值应与实际的应用程序运行环境相匹配。根据应用程序的需求和可用资源,合理地调整-Xmx的值可以确保程序的稳定运行并优化性能。在设置-Xmx参数时,应考虑程序的内存需求、垃圾回收机制以及其他相关因素。


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了运行时数据区、堆介绍、堆的关键参数等内容,希望对大家有所帮助。

相关文章
|
1月前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
39 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
27天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
211 1
|
8天前
|
NoSQL Java Redis
秒杀抢购场景下实战JVM级别锁与分布式锁
在电商系统中,秒杀抢购活动是一种常见的营销手段。它通过设定极低的价格和有限的商品数量,吸引大量用户在特定时间点抢购,从而迅速增加销量、提升品牌曝光度和用户活跃度。然而,这种活动也对系统的性能和稳定性提出了极高的要求。特别是在秒杀开始的瞬间,系统需要处理海量的并发请求,同时确保数据的准确性和一致性。 为了解决这些问题,系统开发者们引入了锁机制。锁机制是一种用于控制对共享资源的并发访问的技术,它能够确保在同一时间只有一个进程或线程能够操作某个资源,从而避免数据不一致或冲突。在秒杀抢购场景下,锁机制显得尤为重要,它能够保证商品库存的扣减操作是原子性的,避免出现超卖或数据不一致的情况。
40 10
|
21天前
|
Java
JVM运行时数据区
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一
22 2
|
26天前
|
存储 监控 Java
合理设置JVM堆大小
合理设置JVM堆大小
31 4
|
27天前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
21 3
|
24天前
|
存储 IDE Java
实战优化公司线上系统JVM:从基础到高级
【11月更文挑战第28天】Java虚拟机(JVM)是Java语言的核心组件,它使得Java程序能够实现“一次编写,到处运行”的跨平台特性。在现代应用程序中,JVM的性能和稳定性直接影响到系统的整体表现。本文将深入探讨JVM的基础知识、基本特点、定义、发展历史、主要概念、调试工具、内存管理、垃圾回收、性能调优等方面,并提供一个实际的问题demo,使用IntelliJ IDEA工具进行调试演示。
24 0
|
2月前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
2月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
40 4
|
17天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。