JAVA的内存结构

简介: JAVA的内存结构

一、JAVA内存结构和JAVA内存模型JMM的区别

面试过很多人,这两个概念都分不清楚。JAVA内存结构一般是指JVM运行代码时会将自己管理的内存分成几个运行时数据区,这些运行时数据区包括方法区、虚拟机栈、本地方法栈、堆和程序计数器。而Java内存模型只是一种规范,抽象的概念,不是具体存在的。java内存模型规定以下几点:

1、所有的变量存储在主内存中。

2、每一个线程都有自己的工作内存,且对变量的操作都是在自己的工作内存中进行的。

3、不同的线程之间无法直接访问彼此工作内存中的变量,要想访问只能通过主存来传递(线程通信)。

JAVA内存模型JMM规定将所有的变量(不包括局部变量)都存放在公共内存中,当线程使用变量时会把主存的变量复制到自己的工作空间(私有内存),线程对变量的读写操作,都是在自己的工作空间内完成的。操作完成之后,再把自己私有内存的变量刷新到主内存中。

二、JAVA运行时数据区

方法区

方法区是一块各个线程共享的区域,主要存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。根据《Java虚拟机规范》,方法区逻辑上属于堆的一部分,为了和堆区分又叫做非堆(Non-Heap)。

方法区只是JVM的一个规范,所有虚拟机必须要遵守。但是并没有严格要求如何实现。而在JDK7之前,Hotspot实现的方式是永久代,而在jdk7的时候,已经把原本放在永久代的字符串常量池、静态变量等移到堆中,而到了JDK8完全废弃了永久代的概念,而改为使用与JRockitJ9一样使用元空间来实现方法区。而元空间使用的是本地内存实现的,也就是说它不再受限于Jvm分配内存(jdk7之前使用-XXMaxPermSize=16m 设置永久代的大小)大小的限制,理论上物理机还有内存就可以分配,这样从一定程度上避免了OutOfMemoryError(OMM)异常。

Java虚拟机栈

Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈可以理解成我们平时的一个Java方法,每个方法被执行的时候,JAVA虚拟机都会创建一个栈帧用来存放局部变量表、操作数栈、动态链接、方法出口(方法返回)等信息。

1、局部变量表

局部变量表存放了编译期可知的Java虚拟机的基本数据类型(booleanbytecharshortintfloatlongdouble)、对象引用(reference)和returnAddress类型。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量标的大小(这里的大小是指变量槽的数量,而一个变量槽的大小是由虚拟机自行决定的,比如一个槽占用32bit或者64bit或者更多)。

2、操作数栈

后进先出LIFO,最大深度由编译期确定。栈帧刚建立使,操作数栈为空,执行方法操作时,操作数栈用于存放JVM从局部变量表复制的常量或者变量,提供提取,及结果入栈,也用于存放调用方法需要的参数及接受方法返回的结果。操作数栈可以存放一个jvm中定义的任意数据类型的值。在任意时刻,操作数栈都一个固定的栈深度,基本类型除了64位长度的longdouble占用两个深度,其它占用一个深度。

3、动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用,一部分会在类加载阶段或第一次使用的时候转化为直接引用(如finalstatic域等),称为静态解析,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。

4、方法返回地址

当一个方法被执行后,有两种方式退出该方法:执行引擎遇到了任意一个方法返回的字节码指令(lreturnfreturndreturn以及areturn)或遇到了异常,并且该异常没有在方法体内得到处理。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。

方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,如果有返回值,则把它压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是基本一致的,区别在于虚拟机栈执行的是虚拟机中的JAVA方法,而本地方法栈执行的是Native方法。

JAVA

JAVA堆是虚拟机所管理内存的最大一块区域,是线程共享的一块区域。而几乎所有的对象实例都是在堆中分配内存。而且堆也是垃圾回收主要回收的区域,堆又被分成新生代老年代永久代“Eden“From Survivor”“To survivor”

默认情况下JVM默认的最大内存-Xmx为物理机内存的1/4,比如我的计算机是24G的那么最大内存大概就是6G,而初始化内存是物理机内存的1/64,也就是384M。你可能说你不信,那么看代码。

public class Test {


   public static void main(String[] args){

       long maxMemory = Runtime.getRuntime().maxMemory() ;//Java 虚拟机试图使用的最大内存量。

       long totalMemory = Runtime.getRuntime().totalMemory() ;//Java 虚拟机中的内存总量,初始化内存。

       System.out.println("最大内存Xmx为:" + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB"); //6120MB

       System.out.println("初始化内存Xms为: "+ totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB"); //384MB

       while (true){


       }

   }

}


输出的最大内存为6120MB大概就是6G,而刚运行的内存为384MB,也可以理解为初始化内存Xms。

还可以通过JAVA VisulVM可以看到 402,653,216B大概也是384MB。

JVM中堆的空间是可以配置的,使用-Xmx-Xms可以配置最大内存和初始化内存,但是一般我们会考虑将初始化内存与最大内存设置成一样,避免在初始化内存扩容时发生对象的移动。当然当Java堆中没有足够的空间来存放对象时就会发生OOM异常。

程序计数器

程序计数器是一块较小的内存空间,几乎可以忽略不计,是运行速度最快的存储区域。它可以理解为当前线程执行的字节码行号。在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址。如果是在执行native方法,则是未指定值(undefined)。它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。字节码解释器工作时就是通过改变这个计数器的值来选取下–条需要执行的字节码指令。它是唯一一个在Java虚拟机规范中没有规定任何OOM情况的区域。

相关文章
|
26天前
|
存储 缓存 安全
Java内存模型深度解析:从理论到实践####
【10月更文挑战第21天】 本文深入探讨了Java内存模型(JMM)的核心概念与底层机制,通过剖析其设计原理、内存可见性问题及其解决方案,结合具体代码示例,帮助读者构建对JMM的全面理解。不同于传统的摘要概述,我们将直接以故事化手法引入,让读者在轻松的情境中领略JMM的精髓。 ####
33 6
|
16天前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
21 0
|
1月前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
27天前
|
存储 算法 Java
Java内存管理深度剖析与优化策略####
本文深入探讨了Java虚拟机(JVM)的内存管理机制,重点分析了堆内存的分配策略、垃圾回收算法以及如何通过调优提升应用性能。通过案例驱动的方式,揭示了常见内存泄漏的根源与解决策略,旨在为开发者提供实用的内存管理技巧,确保应用程序既高效又稳定地运行。 ####
|
19天前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
31 8
|
16天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
21天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
46 5
|
19天前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
19天前
|
安全 Java 程序员
Java内存模型的深入理解与实践
本文旨在深入探讨Java内存模型(JMM)的核心概念,包括原子性、可见性和有序性,并通过实例代码分析这些特性在实际编程中的应用。我们将从理论到实践,逐步揭示JMM在多线程编程中的重要性和复杂性,帮助读者构建更加健壮的并发程序。
|
23天前
|
JSON Java 程序员
Java|如何用一个统一结构接收成员名称不固定的数据
本文介绍了一种 Java 中如何用一个统一结构接收成员名称不固定的数据的方法。
25 3
下一篇
DataWorks