Java—JVM II

简介: Java—JVM II

JVM

Java 程序中的内存分配和回收都由 JVM 管理,不支持程序员直接对内存地址进行操作。不容易出现内存泄漏和内存溢出问题。


内存空间

进程是分配资源的基本单位。除方法区外,所有数据均存放在给进程分配的内存块中。

Java 程序的内存空间主要分为以下几个区域:

网络异常,图片无法展示
|

方法区

(Method Area) 存放 Java 程序的二进制代码,即类的信息

在虚拟机启动时直接存入系统内存中,大小只受系统内存的限制。

堆区

(Heap) 存放进程创建的对象实例和数组

由线程共享,在虚拟机启动时为进程创建,是占用内存最大的区域。

常量池

存放 final 常量、static 变量和方法,以及 String 和部分包装类型的对象(一经赋值不再改变)

JDK 1.7 后 JVM 将常量池从方法区中移出,改为在堆中开辟空间存放。

栈区

(Stack) 存放线程执行 Java 方法调用的内存数据。线程每执行一个方法都会创建一个栈帧放入栈区,在方法执行结束后弹出。栈帧中储存局部变量表、操作数栈、动态链接、方法出口信息。其中局部变量表存放线程和方法的参数、方法中定义的基础类型变量和对象引用

由线程私有。生命周期随着线程的创建而创建,随着线程的结束而死亡。

栈区分为虚拟机栈(执行 java 方法)和本地方法栈(执行 native 方法,底层用 C 实现),但在 HotSpot 虚拟机中两者合二为一。

程序计数器

用来记录线程的运行状态、管理线程的运行。

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
  2. 在多线程的情况下程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够继续运行。

由线程私有。生命周期随着线程的创建而创建,随着线程的结束而死亡。


对象的内存分配

分配空间

Hotspot 虚拟机中,对象在内存中分为 3 块区域:对象头、实例数据和对齐填充。

  • 对象头负责记录对象信息,包括对象运行数据(哈希码、GC 分代年龄、锁状态标志等)和 类型指针(对象所属的类)。
  • 对象起止地址必须是 8 字节整数倍,空余部分将使用对齐填充来补全。

分配过程

在 Java 中创建对象,需要经过以下过程:

Step 1. 类加载检查

JVM 接收到 new 指令,首先检查该类是否在常量池中,然后检查该类是否已被加载、解析和初始化。
如果没有则先加载类,类加载器会将类的字节码文件放入 JVM 解析。Copy to clipboardErrorCopied
复制代码

Step 2. 分配内存

类加载后,会从 Java 堆中划分一块固定大小的内存区域分配给对象。Copy to clipboardErrorCopied
复制代码

Step 3. 半初始化

内存分配完成后,虚拟机将对象的实例数据都半初始化为默认值。此时对象已经可用。Copy to clipboardErrorCopied
复制代码

Step 4. 设置对象头

初始化内存空间后,虚拟机要对对象进行必要的设置,把信息存放在对象头中。Copy to clipboardErrorCopied
复制代码

Step 5.初始化对象

新的对象已经产生,但所有的字段都为默认值。接下执行 init 方法,将对象的实例数据赋值。Copy to clipboardErrorCopied
复制代码

Step 6. 建立关联

将对象引用指向创建的对象。该对象可以通过引用被使用。Copy to clipboardErrorCopied
复制代码

在创建对象的过程中可能会发生指令重排序。如果尚未完全初始化就建立了关联,其他线程读取该对象可能会读取到默认值。

分配方式

内存划分有以下两种分配方式:

  • 指针碰撞:(内存规整时) 已使用内存和未使用内存用指针隔开,需要分配内存时将指针移动一定位置。
  • 空闲列表:(内存不规整时)虚拟机维护一个列表记录空闲内存块,需要分配内存时选择合适的内存块。

Java 堆是否规整,取决于垃圾收集算法是否整理内存。


常量池内存分配

String 类

String 对象创建后一经赋值不再改变,有以下两种创建方式:

  1. 直接赋值:如果常量池没有,则在常量池新建对象。否则直接使用常量池中已有对象,引用指向常量池。
  2. 构造方法:如果常量池没有,则在常量池新建对象。无论如何一定会在堆区创建对象,引用指向堆区。
String str1 = "string";                       // 引用指向常量池
String str2 = "str" + "ing";                  // 引用指向常量池
String str3 = new String("string");           // 引用指向堆区
String str4 = str1 + str2;                    // 引用指向堆区
System.out.println(str1 == str2);             // true
System.out.println(str1 == str3);             // falseCopy to clipboardErrorCopied
复制代码

包装类型

JVM 将部分常用的包装类型数据提前缓存在常量池中,用户创建该类对象时直接在常量池取用,而不用真正新建对象。

  • 布尔包装类 Boolean:将全部缓存数据保存在常量池中。
  • 字符包装类 Character:将 ASCII 字符(0-127) 数据缓存保存在常量池中。如果是汉字等其他字符仍然会创建对象。
  • 整型包装类 Byte/Short/Integer/Long:将数值 [-128,127] 内的数据缓存保存在常量池中。但是超出此范围仍然会去创建新的对象。
  • 浮点数类型包装类 Float/Double: 没有实现常量池技术。

内存过载

随着 Java 程序的使用,所占用和分配的内存将会越来越大。如果 JVM 已无法分配足够的内存,将会抛出 OutOfMemoryError 导致程序崩溃。

同时为了更合理地使用内存,Java 会由虚拟机对堆区内存空间自动进行垃圾回收。


目录
相关文章
|
4月前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
3月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
87 0
|
2月前
|
存储 监控 算法
Java JVM 面试题
Java JVM(虚拟机)相关基础面试题
|
3月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
3月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
4月前
|
机器学习/深度学习 监控 算法
Java虚拟机(JVM)的垃圾回收机制深度剖析####
本文深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法、性能调优策略及未来趋势。通过实例解析,为开发者提供优化Java应用性能的思路与方法。 ####
84 1
|
4月前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
195 1
|
4月前
|
监控 Java 开发者
Java虚拟机(JVM)深度优化指南####
本文深入探讨了Java虚拟机(JVM)的工作原理及其性能优化策略,旨在帮助开发者通过理解JVM的内部机制来提升Java应用的运行效率。不同于传统的技术教程,本文采用案例分析与实战技巧相结合的方式,为读者揭示JVM调优的艺术。 ####
99 8
|
5月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
89 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
5月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
76 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))