您现在的位置是:亿华云 > 热点
java内存管理机制剖析(一)
亿华云2025-10-04 03:53:24【热点】7人已围观
简介最近利用工作之余学习研究了一下java的内存管理机制,在这里记录总结一下。1.1 java内存区域当java程序运行时,java虚拟机会将内存划分为若干个不同的数据区域,这些内存区域创建和销毁的时间各
最近利用工作之余学习研究了一下java的内存内存管理机制,在这里记录总结一下。管理
1.1 java内存区域
当java程序运行时,机制java虚拟机会将内存划分为若干个不同的剖析数据区域,这些内存区域创建和销毁的内存时间各不相同,所承担的管理功能也不相同,他们各司其职,机制各尽所责。剖析这些区域的内存划分如下图

运行时数据区主要有五个区,分别是管理 堆 ,方法区,机制虚拟机栈,剖析本地方法栈,内存程序计数器,管理下面我来一一详细讲解这五个数据区
堆java堆是机制java虚拟机管理内存中最大的一块,它是被所有线程共享的一块内存区域,在虚拟机启动时创建,此内存的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在堆分配内存。
java虚拟机规定,云南idc服务商java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可。在实现时,既可以实现固定大小的,也可以是扩展的,可以通过配置-Xmx和-Xms来扩展大小。如果堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError
方法区方法区也是被所有线程共享的一块内存区域,在Java虚拟机规范中,方法区是堆的逻辑组成部分,但他又被与堆区分开来,别名称为Non-Heap,它主要的存储内容有下面几点
类型的完整有效名类型直接父类的完整有效名类型的修饰符(public,abstract,final的某个子集)类型的常量池域(Field)信息方法(Method)信息 除了常量外的所有静态(static)变量总结起来就是主要用于存储已被虚拟机加载的类信息,站群服务器常量,静态变量,编译器编译后的代码等数据
这里我在介绍一下常量池,域信息和方法信息
—-常量池常量池也称为运行时常量池(Runtime Constant Pool),用于存放编译期生成的各种字面量和符号引用,它是这个类型用到的常量的一个有序集合,包括实际的常量(String, Integer, 和Floating point常量)和类型,域和方法的符号引用
。
池中的数据项像数组项一样,是通过索引访问的。 因为常量池存储了一个类类型所使用到的所有类型,域和方法的符号引用,所以它在java程序的动态链接中起了核心的作用 —-域(Field)信息域的相关信息包括:域名; 域类型; 域修饰符(public, private, protected,static,final volatile,transient的某个子集)
—-方法(Method)信息方法的相关信息包括:方法名, 方法的返回类型(或 void), 方法参数的数量和类型(有序的服务器托管),方法的修饰符(public, private, protected, static, final, synchronized, native, abstract的一个子集),除了abstract和native方法外,其他方法还有保存方法的字节码(bytecodes)操作数栈和方法栈帧的局部变量区的大小。
java虚拟机规范对方法区的限制比较宽松,除了和java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不是实现垃圾收集。垃圾收集行为在方法区也比较少出现,当方法区无法满足内存分配时,会抛出OutOfMemoryError
虚拟机栈虚拟机栈是线程私有的,它的生命周期与线程相同,当我们start一个线程时,jvm会为当前线程开辟一块虚拟机栈,当当前线程死亡时,线程的虚拟机栈也会销毁。
代码中每个方法在执行的同时,都会创建一个栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用和returnAddreass类型
JVM对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出SstackOverFlowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryErro异常
本地方法栈本地方法栈和虚拟机栈的作用是一样的,只不过本地方法栈是虚拟机执行java方法时开辟的栈,而本地方法栈是虚拟机用到Native方法时,开辟的栈。
程序计数器程序计数器是一块较小的内存,它可以看作是当前线程的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取吓一跳需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能。
由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程指尖计数器互不影响,独立存储。
1.2 对象创建
了解了内存的数据区域,我们可以进一步了解对象是如何创建的了。这里先通过一张流程图一窥java的对象创建过程

加载完成后,便会在堆中为对象分配内存,JVM有两种分配方式①指针碰撞,②空闲列表,下面我详细讲讲这两种分配方式。
—-指针碰撞当java堆中内存是整齐的,所有用过的内存都放一边,空闲的内存放在另一边,中间放着一个指针座位分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边摞动一段与对象大小相等的距离,这种分配就叫指针碰撞
—-空闲列表当Java堆的内存并不是完整的,已分配的内存和空闲内存相互交错,JVM通过维护一个列表,记录可用的内存块信息,当分配操作发生时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。这种分配方式称为空闲列表
当JVM所采用的垃圾收集器带有压缩整理功能时,java堆是规整的,这个时候会采用指针碰撞分配内存,否则会采用空闲列表分配内存。对象创建是一个非常频繁的行为,进行堆内存分配时还需要考虑多线程并发问题,可能出现正在给对象A分配内存,指针或记录还未更新,对象B又同时分配到原来的内存,解决这个问题有两种方案:
1、采用CAS保证数据更新操作的原子性;2、把内存分配的行为按照线程进行划分,在不同的空间中进行,每个线程在Java堆中预先分配一个内存块,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB);很赞哦!(924)
相关文章
- 因为域名解析需要同步到DNS根服务器,而DNS根服务器会不定时刷,只有DNS根服务器刷新后域名才能正常访问,新增解析一般会在10分钟左右生效,最长不会超过24小时,修改解析时间会稍微延长。
- 基于Spring Cloud的微服务落地
- 编程初学者必须建造的几根“支柱”
- 人工智能技术越来越火 用哪个开发语言更好
- 公司名字不但要与其经营理念、活动识别相统一,还要能反映公司理念,服务宗旨、商品形象,从而才能使人看到或听到公司的名称就能产生愉快的联想,对商店产生好感。这样有助于公司树立良好的形象。
- Java面试中,遇到这类面试题最吃亏!
- 在工程领域中,机器学习的数学理论基础尤为重要
- Python登顶年度编程语言排行榜 榜尾垫底者仅得0分
- ICANN 规章禁止转移已经被记录或者在60天前内转移的域名。
- 如何优雅的「打断」你的线程?