`

4、内存管理机制---运行时数据区

 
阅读更多

第二部分、内存管理机制

1、运行时数据区

2、内存溢出异常

3、垃圾收集器

4、内存分配策略

5、内存调优分析

 

Java的内存管理就是对象的分配和释放问题。

         分配 :内存的分配是由程序完成的,程序员需要通过关键字new (或者反射newinstance)为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。
         释放 :对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时加重了JVM的工作。因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。

 

JVM主要包含三大核心部分:运行时数据区,类加载器和执行引擎。

根据《Java虚拟机规范(第2版)》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域:

 

1、程序计数器(Program   Counter  Register )

         程序计数器是一块较小的线程私有内存,它的作用是记录 当前线程所执行的字节码行数。解释器java.exe通过改变计数器值来选取下一条字节码指令;

         java多线程是通过线程切换来实现几个线程同时进行,但一个处理器(对于多核处理器来说是一个内核)只会执行一条线程,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器;

         如果线程执行的是一个Java方法,计数器记录的是字节码语句的地址;如果执行的是Natvie方法,计数器值自设为空(Undefined)。

 

2、Java虚拟机栈(Java Virtual Machine  Stacks)

         虚拟机栈也是线程私有的,虚拟机栈描述的是Java方法执行时的内存模型:每个方法被执行时会创建一个栈帧(StackFrame)用于存储局部变量表、操作数栈、动态链接、方法出口(见16章,字节码执行引擎)等信息。一个方法被调用到完成,就对应着一个栈帧  在虚拟机栈中  入栈和出栈。

         局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、reference类型(指针或句柄)和returnAddress类型。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

         局部变量表(参数和局部变量)是线程私有的;

         虚拟机栈会出现的两种异常:线程请求深度大于允许深度,将抛出StackOverflowError异常;如果虚拟机栈支持动态扩展(Groovy)当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

 

3、本地方法栈(Native  Method  Stacks)

         本地方法栈同样也是线程私有的,与虚拟机栈作用是相似,区别是本地方法栈只为Native方法服务。虚拟机规范中对本地方法栈强制规定,虚拟机可以自由实现它。甚至有的虚拟机(HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

 

4、Java堆(JavaHeap)

          堆是占jvm内存最大的一块,Java堆是所有线程共享的一块内存,在虚拟机启动时创建。该内存的唯一目的就是存放对象实例,所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。

          

          堆是垃圾收集器的主要工作区域,也被称做“GC堆”(Garbage Collected Heap)。如果从内存回收的角度看,现在收集器基本采用分代收集算法,所以Java堆可以细分为:新生代和老年代;再细致一点的有Eden空间、FromSurvivor空间、ToSurvivor空间等。如果从内存分配的角度看,Java堆可以分线程私有缓冲区(ThreadLocalAllocationBuffer,TLAB)。

          根据Java虚拟机规范的规定,Java堆可以划分在物理上不连续的内存空间中,只要逻辑上是连续的即可,就像磁盘空间一样。在实现时,heap即可以是固定大小的,也可以是可扩展的,当前主流的虚拟机都是可扩展的(通过-Xmx和-Xms控制)。如果在堆中没有内存完成实例分配,且也无法再扩展时,将会抛出OutOfMemoryError异常。

 

5、方法区(Method Area)

         方法区也是所有线程共享内存,它用于存储(已被)虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

         

         在HotSpot虚拟机上习惯称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。对于其他虚拟机(如BEAJRockit、IBMJ9等)来说是不存在永久代的概念的。即使是HotSpot虚拟机本身,根据官方发布的路线图信息,现在也有放弃永久代并“搬家”至NativeMemory来实现方法区的规划了。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

 

#方法编译后存放位置?在方法区,线程共享,执行时在jvm栈。 

 

6、运行时常量池(Runtime Constant Pool)是方法区的一部分

        存放方法执行时的符号引用(不是直接引用,在字节码执行引擎时转换为直接引用,一般编译时生成,如果支持动态扩展,在执行引擎时也可以生成符号引用;

        字节码常量池是静态的,运行时常量池可能是动态的,但他们的初始是相同的;

7、直接内存(Direct Memory)

       直接内存不是jvm管理的内存,是java NIO使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。避免在Java堆和Native堆中来回复制数据,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括RAM及SWAP区或者分页文件)的大小及处理器寻址空间的限制。服务器管理员配置虚拟机参数时,一般会根据实际内存设置-Xmx等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制(包括物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

 

 

$对象的创建、布局、访问:

 

#对象的创建(详见类加载机制):        

        创建对象通常是一个new关键字生成,在jvm中,对象创建是怎样实现的呢?

        对象占用内存大小 是在编译后确定的还是在类加载完后确定的?

        jvm遇到一条new指令时(没有被初始化的情况),首先去通过这个指令的参数 在运行时常量池中找到这个类的符号引用,再执行类加载(加载、验证、准备、解析、初始化);执行类加载就会为新生的对象实例分配堆内存,对象所需要的内存大小在类加载完后就完全确定。

 

        如果内存是规整(将空闲和使用中的内存绝对分开,中间位置为临界指针),使用时向空闲区移动,如果不是规整的jvm有一个空闲列表,来记录哪块内存是空闲的,在分配内存时找到一块适合对象大小的内存块给对象使用;jvm的gc算法决定了使用哪种内存分配方式,serial、parnew带compat的采用规整的指针位移,使用CMS基于mark-sweep算法采用空闲列表;

        在创建对象实例的过程,涉及到指针偏移和指向某个内存块的过程,然而在多线程并发的时候,可能会出现线程安全问题,比如:指针正在给new A()分配内存,new B()同时请求指针分配内存,jvm提供两种解决方案:一种是对分配内存空间过程实行同步处理(锁定,即所有实例按顺序分配);另一种是线程预先分配到小块内存,称为本地线程分配缓冲(thread local alloction buffer,TLAB),在线程用完tlab再获得新的tlab时进行同步锁定,显然第二种是常用方法,虚拟机是否使用tlab,通过-xx.+/-usertlab参数设定。

       在对象获得堆内存之后,jvm将对象获得内存都格式化(除对象头),如果使用tlab,这一过程在线程分配内存时完成,这一过程保证新对象即使属性全为空,也不会报空指针异常,在jvm类加载器完成加载之后,为实例分配堆内存及方法区,之后对实例进行必要的配置(记录是哪个类的实例、记录所属类的信息、配置哈希吗、配置对象的gc分代等),这些信息存放在对象头中;完成上面的工作,从jvm角度一个对象的创建成功了,从一个对象来看,生命周期才刚刚开始。

 

#对象的布局:

       在HotSpot(jdk1.3后作为JVM)虚拟机,内存对对象的存储可以分为3块:对象头(header)、数据(instance data)和对齐填充(padding),这里没有说方法区。

       对象头包括两部分:一部分为对象运行时数据(hashcode、gc分代、锁状态、偏向线程、线程锁、偏向时间戳等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是谁的实例,但查找对象元数据并不一定要经过对象本身(稍后详解);若对象时一个对象数组,对象头中还一块来记录数组的长度,因为jvm可以通过元数据确定对象的大小而不能确定数组的大小。

        数据:无论是从父类继承还是子类新增的属性,都需要记录,而记录的顺序与类中定义的顺序是不一致的,记录顺序是根据jvm字段额定长度和类中定义的顺序生成,long/double,int,short/char,byte/boolean,oop(普通对象)相同长度的分配在一起,在满足上面的顺序情况下,父类属性在子类属性之前记录;Hotspot规定对象大小额定为8的倍数,而oop长度可能不是8的倍数,jvm就会采取对齐填充;

 

#对象访问

       创建对象之后,需要访问对象才能完成我们预定的功能,如何实现访问呢?对象访问也是通过jvm实现。

       在jvm栈中讲到reference类型,引用类型简单的讲就是指向堆内存中具体对象的索引,访问方式有句柄方式和指针方式;

 

       1、使用句柄方式:java堆内存中将划分出一块内存来作为句柄池空间,reference实例就是对象的句柄地址,而句柄中包含了对象数据和基本类型的具体栈地址;优点reference中储存的是稳定的句柄地址,在对象被移动(垃圾回收时,移动对象在堆内存中的地址)时只改变句柄中指向对象的指针,而reference不做修改;

2、使用指针方式:reference实例指向堆内存对象实例:优点是节省reference到句柄的指针定位开销,HotSpot就是采用指针方式访问;
 

 

  • 大小: 120.4 KB
  • 大小: 101.8 KB
  • 大小: 92.5 KB
分享到:
评论

相关推荐

    Objective-C 内存管理机制 - iOS知识库1

    Objective-C 是 iOS 开发中的主要编程语言,其内存管理机制对于优化应用性能和防止内存泄漏至关重要。内存管理主要关注如何有效地分配和释放内存,确保程序的稳定运行。 1. **值类型与引用类型:** - **值类型**...

    基于内存的K-V数据平台(Memcached)

    Memcached的源代码简洁明了,对于理解分布式缓存的工作原理和内存管理有很好的学习价值。通过阅读源码,可以深入了解其内部机制,如哈希表的实现、内存分配策略、网络通信细节等。 总结来说,Memcached是一个高效的...

    Linux内存管理机制文档

    ### Linux 2.6 内存管理机制解析 #### 一、地址类型的分类与功能 在深入探讨Linux 2.6版本的内存管理机制之前,我们先了解几个基本概念,尤其是不同类型地址的作用及其如何帮助系统高效地管理和使用内存资源。 **...

    Windows驱动编程视频教程-内存管理--(其他)

    驱动程序通常运行在内核模式下,因此需要理解和掌握内核模式下的内存管理机制。 1. 内核虚拟地址空间:Windows内核模式的地址空间被划分为多个段,如系统保留区、执行代码区、数据区等。驱动程序开发者需要知道如何...

    全面介绍Windows内存管理机制及C++内存分配实例

    Windows内存管理机制负责有效地分配、使用和回收系统内存,以确保多个进程能够高效、安全地共享资源。本文将深入探讨Windows内存管理的关键概念,并结合C++编程语言,通过实例讲解内存分配的实践操作。 1. **...

    linux核心中的内存管理和缓冲机制

    内存管理确保了系统中多个进程的高效、安全运行,而缓冲机制则优化了数据的读写速度,提高了整体性能。 一、Linux内存管理 1. 分区与地址空间:Linux内存分为物理内存和虚拟内存。物理内存是实际的RAM,而虚拟内存...

    内存释放专家--绿色版

    此外,了解操作系统自身的内存管理机制,如Windows的虚拟内存功能,也可以帮助更好地理解和解决内存相关问题。 总的来说,"内存释放专家--绿色版"提供了一个方便的解决方案,用于处理内存泄漏问题,提升系统运行...

    linux 内存运行原理自己的总结

    本文将深入探讨Linux内存运行原理,重点关注SLAB内存管理机制以及如何进行内存泄漏分析。 1. **Linux内存架构** - 物理内存:计算机硬件提供的RAM,用于存储程序和数据。 - 逻辑地址与物理地址:进程看到的是逻辑...

    WINDOWS程序员使用指南(一)----DLL和内存管理

    内存管理是程序设计中的另一个核心部分,Windows提供了一套完整的内存管理机制,包括分配、释放和管理内存空间。 4. **堆内存管理**:HeapAlloc和HeapFree函数用于在进程的默认堆上分配和释放内存。程序员应理解...

    全面介绍Windows内存管理机制.doc

    总结,Windows内存管理机制通过复杂的地址空间划分、映射、分页以及权限控制,确保了程序运行的高效性和安全性。无论是32位还是64位系统,这些机制都是保障操作系统正常运行的基础。理解这些概念对于开发高效且稳定...

    全面介绍Windows内存管理机制及C++内存分配实例.pdf

    ### Windows内存管理机制详解 #### 一、进程地址空间 在深入探讨Windows内存管理机制之前,我们首先需要理解进程在Windows系统中的地址空间是如何划分的。这部分内容将帮助我们更好地理解后续关于内存分配与管理的...

    Android3.0内存管理机制分析.pdf

    《Android 3.0内存管理机制分析》这篇文章深入探讨了Android 3.0操作系统中的内存管理机制,该机制是基于Linux 2.6内核进行优化的。文章着重阐述了在这一版本中内存管理的新技术和改进策略。 Android操作系统由系统...

    内存管理数据结构优化.pptx

    ### 内存管理数据结构优化 #### 内存分配器的性能优化 1. **分段分配** - **概念**:将整个可用内存空间划分为一系列大小相等的段,每一段作为独立的内存区域进行管理和分配。 - **优势**: - 提高内存分配和...

    Apache Spark 内存管理详解

    为了更好地理解和利用Spark的内存管理机制,本文将详细介绍Spark内存管理的关键概念和技术细节。 #### 二、Spark内存管理概述 在执行Spark应用时,集群会启动两种类型的JVM进程:Driver和Executor。Driver进程主要...

    ecos系统内存管理

    其内存管理机制对于确保系统稳定性和提高性能至关重要。本文将深入探讨eCos内存管理的核心概念、原理以及具体实践。 #### 二、静态内存分配 在嵌入式领域,为了更好地控制内存资源,避免不可预知的问题,通常推荐...

    操作系统课程设计 内存管理

    操作系统是计算机系统的核心组成部分,它负责管理和控制硬件...理解并实践这些知识点,对于深入理解操作系统的内存管理机制至关重要。通过这样的课程设计,你可以更好地掌握内存管理的原理,并具备解决实际问题的能力。

    常用数据数据加载到内存中

    标题 "常用数据数据加载到内存中" 涉及的核心知识点是数据的内存管理,这在计算机科学,尤其是编程和软件开发中是非常基础且重要的。数据加载到内存中通常是程序运行过程中不可或缺的一部分,特别是对于大数据处理、...

    java内存分配机制详解

    Java作为一种广泛应用的编程语言,其内存管理机制对于确保程序高效稳定运行至关重要。本文旨在详细介绍Java内存分配机制中的几个关键概念:寄存器、栈、堆、静态域、常量池及其在Java运行时环境中的角色与功能。 ##...

    嵌入式Linux性能详解-Linux内存管理

    Linux内核提供了一系列内存管理机制,包括但不限于: - **分页机制**:将虚拟内存空间分成固定大小的页面,便于管理和分配。 - **内存映射**:允许将文件或其他对象直接映射到进程的虚拟地址空间中。 - **交换机制*...

Global site tag (gtag.js) - Google Analytics