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情况的区域。

相关文章
|
13天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
24天前
|
缓存 easyexcel Java
Java EasyExcel 导出报内存溢出如何解决
大家好,我是V哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。以下是V哥整理的解决该问题的一些常见方法,包括分批写入、设置合适的JVM内存、减少数据对象的复杂性、关闭自动列宽设置、使用Stream导出以及选择合适的数据导出工具。此外,还介绍了使用Apache POI的SXSSFWorkbook实现百万级别数据量的导出案例,帮助大家更好地应对大数据导出的挑战。欢迎一起讨论!
139 1
|
1月前
|
缓存 算法 Java
Java中的内存管理:理解与优化
【10月更文挑战第6天】 在Java编程中,内存管理是一个至关重要的主题。本文将深入探讨Java内存模型及其垃圾回收机制,并分享一些优化内存使用的策略和最佳实践。通过掌握这些知识,您可以提高Java应用的性能和稳定性。
44 4
|
1月前
|
存储 监控 算法
Java中的内存管理:理解Garbage Collection机制
本文将深入探讨Java编程语言中的内存管理,着重介绍垃圾回收(Garbage Collection, GC)机制。通过阐述GC的工作原理、常见算法及其在Java中的应用,帮助读者提高程序的性能和稳定性。我们将从基本原理出发,逐步深入到调优实践,为开发者提供一套系统的理解和优化Java应用中内存管理的方法。
|
8天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
29 6
|
12天前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
33 2
|
13天前
|
存储 安全 Java
什么是 Java 的内存模型?
Java内存模型(Java Memory Model, JMM)是Java虚拟机(JVM)规范的一部分,它定义了一套规则,用于指导Java程序中变量的访问和内存交互方式。
35 1
|
23天前
|
存储 算法 Java
聊聊jvm的内存结构, 以及各种结构的作用
【10月更文挑战第27天】JVM(Java虚拟机)的内存结构主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和运行时常量池。各部分协同工作,为Java程序提供高效稳定的内存管理和运行环境,确保程序的正常执行、数据存储和资源利用。
45 10
|
19天前
|
存储 运维 Java
💻Java零基础:深入了解Java内存机制
【10月更文挑战第18天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
26 1
|
22天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。