堆
一个java应用在运行中所创建的所有类实例或数组都放在了同一个堆中,并由应用所有的线程共享。因为一个java应用
唯一对应了一个jvm实例,所以每个应用都独占了一个堆,它不可能对另一个应用的堆进行破坏。然而,一个多线程应用必须考虑同步问题。
jvm有在堆中分配对象的指令,却没有释放对象的指令。正如你无法用java代码去释放一个对象一样,字节码也没有对应的
功能。应用本身不用去考虑何时和用什么方法去回收不用对象所占用的内存。通常,jvm把这个任务交给垃圾收集器。
垃圾收集
一个垃圾收集器的主要工作是回收不再被引用的对象所占用的内存。它也可能去移动仍然使用的对象以减少内存碎片。
jvm规范没有指定垃圾收集使用什么技术,这些都由jvm的实现者去定夺。因为对象的引用可能存在很多地方,如java堆栈,堆,方法区,native方法栈。所以垃圾收集技术的使用在很大程度上影响了运行数据区的设计。象方法区一样,堆不必是一块连续的内存区,也可以根据应用的需要动态调整大小。可以把方法区放在堆的顶部,换句话说就是类型信息和实际
对象都在同一个堆上。负责清理对象的垃圾收集器可能也要负责类的回收。堆的初始化大小,最大最小尺寸可以由用户或程序指定。
对象表现( Object Representation)
(译者:C++中称为对象模型)jvm规范没有规定对象在堆中该如何表现。对象的表现会影响堆和垃圾收集的整个设计,它由jvm的实现者决定。对象的主要数据是由对应类和其父类声明的实例变量组成(instance variables 译者:对应Class variables, Class variables存储在方法区中,这在上篇译文中有讲)jvm应该既能够从一个对象引用快速的找到实例变量,也能够快速的找到存储在方法区中的类数据。所以在对象中常常会有一个指向
方法区的指针。
一个可能的实现是把堆分成两部分:一个句柄池和一个对象池。如图5-5一个对象引用是一个指向句柄池的native指针。句柄池的每个条目有两部分:一个指向对象实例变量的指针,一个指向方法区类型数据的指针。这种设计的好处是利于堆碎片的整理,当移动对象以减少碎片时不用更新每个对象引用而只更改句柄就可以了。缺点是每次访问对象都要经过两次
指针传递。
另一种设计是使对象指针直接指向对象实例变量,而在对象实例内包含一个指向方法区类型数据的指针。这样的设计的优缺点正好与前面的方法相反。如图5-6.
jvm有若干理由使它能够从对象引用中得到对应类的数据。
1。 当应用试图转型(cast)时,jvm需要保证要转的类型是此类型本身
或是这个类型的父类型。
2。 当应用进行 instanceof 操作时
3。 当应用激活一个实例方法时,jvm必须进行动态绑定,而它所依赖的信息
不是这个引用的类型,而是这个对象对应的类的信息。 不管对象以什么形式表现,好像都有一个能够方便访问的方法表。由于方法表能加速实例方法的调用,所以对jvm的性能有重要的影响。jvm规范并没有规定必须要使用方发表,例如在内存稀少的环境下,可能不能负担方法表的内存支出。
然而如果使用了方法表,它就应该能够快速的从一个对象引用中获得。图5-7显示了一种连结方法表和对象引用的实现。每个对象的数据包含一个指向特殊数据结构的指针,这个数据结构位于方法区,它包括两部分:
一 一个指向方法区对应类数据的指针
二 此对象的方法表
方法表的每一项都是一个指向方法数据的指针,方法数据包括:
一 此方法的操作数堆栈和局部变量区的大小
二 方法的字节码
三 异常表
这些信息足够jvm去激活一个方法了。方法表的函数指针包括类或其父类声明的函数。也就是说,方法表所指向的函数可能是此类声明的,也可能是它继承下来的。
如果你熟悉c++的内部工作原理,你会发现这和c++的vtbl非常相似。在c++中,对象由实例数据和一组指向虚拟函数的指针组成,jvm也可以采用这种方法。jvm可以在堆中为每个对象都附加一个方法表,这样较之图5-7会占用更多的内存,
但可提高一些效率。这个方案适用在内存足够充裕的系统。(译者:总觉得作者对c++有些误解,c++的对象模型在函数表上的设计和图5-7是类似的,在有虚拟函数的情况下(不考虑多继承),每个对象也只多出一个指向vtable的指针,而vtable也是与类关联的。)
除了图5-5和5-6显示的实例数据外,对象数据还有一个逻辑部分,那就是对象锁(object's lock)。在jvm中每个对象都有一个锁,以用于多线程访问时的同步。在某个时刻只有一个线程拥有这个对象锁,而且只有这个线程才可以对对象的数据进行访问。其他要访问这个对象的线程只有等待,直到拥有对象锁的线程释放所有权。当一个线程拥有对象锁后,可以继续对锁追加请求。但请求几次,必须对应释放几次。
许多对象在其生命期内可能不需要加锁,这样也不需要附加数据,正如图5-5 5-6所示,对象数据内没有一个指向锁数据(lock data)的指针。而只有当需要加锁的时候才分配对应锁数据,但这时需要其他的方法来联系对象数据和对应的锁数据,例如把锁数据放在一个以对象地址为索引的树中。除了实现锁需要的数据,每个java对象逻辑上还有为实现同步而添加的数据。
锁是用来实现多个线程对共享数据的互斥访问,而同步则是实现多个线程为完成一个共同目标而协调工作。同步由等待方法和通知方法共同实现。每个类都从Object那里继承了三个等待方法(三个名为wait()过载函数)和两个通知方法(notify()
和notifyAll())。当一个线程在一个对象上调用wait方法,jvm就阻塞了这个线程并把它放在了这个对象的等待集(wait set)中。当有一个线程在这个对象调用了通知方法,jvm就会在将来的某个时间唤醒一个或多个在等待集中阻塞的线程。正像锁数据一样,并不是每个对象都需要同步数据。许多jvm实现都把同步数据与对象数据分开,只有在需要时才为此对象创建同步数据,一般是在第一次调用等待方法或通知方法时。
最后,一个对象还可能要包含与垃圾收集有关的数据。垃圾收集必须要跟踪每个对象,这个任务不可避免的要附加一些数据,数据的类型要视垃圾收集的算法而定。例如,假如垃圾收集使用标志清除算法,必须要一个数据来标志此对象是否被引用。像线程锁一样,这些数据也可以放在对象外。一些垃圾收集技术只在运行时需要额外数据。例如标志清除算法使用一个位图来标志对象的引用情况。除了标志对象的引用情况外,垃圾收集还要区分一个对象是否调用了finalizer。在收集一个对象之前,垃圾收集器必须调用声明了finalizer的类的对象。java语言规范指出垃圾收集器对某个对象只能调用finalizer一次,在finalizer中允许这个对象复生(resurrect),即使之再次被引用。这样当这个对象再次被收集时,就不再调用finalizer了。需要finalizer的对象不多,而复生的对象更少,所以对一个对象回收两次的情况很少见。这样用来标志
finalizer的数据虽然逻辑上是对象的一部分,但通常与对象分开保存。
数组表现
再java中,数组是一个成熟的对象。像其他对象一样,数组也存储在堆上,jvm实现的设计者也有权决定数组的表现。
数组也有一个相关的类实例(Class instance),所有具有相同维度和类型的数组同为一个类,而不管数组的长度(多维数组每一维的长度)。
例如一个有三个ints的数组和一个有六个ints的数组都是同一个类。数组的长度只与实例数据有关。数组类的名称由两部分组成,一个是用'['表示的维和一个字符表示的类型。
例如,类型为ints的一维数组的类名为“[I”。类型为bytes的三维数组为“[[[B”。类型为Object的二维数组为“[[Ljava.lang.Object”。多维数组被表示为数组的数组。例如,类型为ints的二维数组,将表示为
一个一维数组,数组元素是一个一维ints数组的引用。如图5-8每个数组必须保存的数据是数组的长度,jvm必须能够从一个数组的引用得到此数组的长度,通过下标访问数组元素,检查数组下标是否越界,激活Object声明的方法。
分享到:
相关推荐
在eclipse设置JVM heap 的最小值与最大值的图案
1. **堆转储分析**:HeapAnalyzer可以读取由JVM生成的heap dump文件,这是在特定时刻Java堆的快照。通过这个快照,它可以显示所有对象及其引用关系,以便找出那些占用大量内存但未被释放的对象。 2. **对象概览**:...
Java虚拟机(JVM)垃圾回收(GC)是Java语言内存管理的核心机制,负责回收Java堆内存中不再使用的对象所占的空间。在JVM GC原理和heapsize调优的学习和实践过程中,需要理解多个关键概念和操作步骤,下面详细展开: ...
### Java VM Heap 堆分析知识点详解 #### JVM内的内存管理 Java虚拟机(JVM)在执行Java程序的过程中,会负责内存的分配与回收。内存管理主要包括对象的创建、存储以及垃圾回收等过程。 - **Root Set 和对象的...
HeapAnalyzer是一款Java内存分析工具,由IBM开发,它可以帮助开发者检查和分析Java堆内存的状态,找出可能存在的内存泄漏或者过度占用内存的对象。通过分析heap dump文件,HeapAnalyzer可以展示对象的分布情况,识别...
Java堆内存分为两部分:JVM heap和Native heap。JVM heap由GC管理,可以通过参数调整大小,而Native heap则与操作系统紧密相关,通常用于JIT编译器的缓冲区、GC的数据结构、JNI调用的对象以及图形库所需的缓冲区等。...
Java.lang.OutOfMemoryError: Java heap space 是 Java 中的一个常见错误,它发生时,Java 虚拟机 (JVM) 无法分配对象,因为堆空间不足。下面是解决该问题的一些方法: 原因分析 1. Java 虚拟机 (JVM) 内存过小:...
1. **堆(Heap)**:这是Java对象的主要存储区域,分为年轻代(Young Generation)和老年代(Tenured Generation)。年轻代又细分为Eden区、Survivor区(S0和S1),新生的对象主要在Eden区分配,经过几次垃圾收集后...
首先,heapdump是一个包含Java虚拟机(JVM)堆内存快照的文件,它记录了程序运行时所有对象以及它们之间的引用关系。通过分析heapdump文件,我们可以找出占用内存较大的对象,追踪内存泄漏的源头,以及了解对象生命...
Java虚拟机(JVM)是Java程序运行的基础,它将内存划分为多个区域,其中堆内存(Heap)和栈内存(Stack)是最基础且重要的两个部分。了解它们的区别对于优化程序性能至关重要。 栈内存主要用于存储程序运行过程中的...
然后,作者对 JVM Heap 的管理进行了详细的讲解,包括 Heap 的结构、 Heap 的参数设置、GC 机制等。 在 JVM Heap 的管理中,作者详细讲解了 Heap 的结构,包括年轻代、年老代和持久代三个部分。并且,作者还讲解了 ...
Heap dump文件是Java虚拟机(JVM)在特定时间点生成的一种文件,它包含了JVM堆内存中的所有对象及其引用关系、类信息、垃圾收集信息等。当程序运行时遇到内存问题,如频繁的垃圾回收或内存溢出,生成heap dump可以...
2. **最大堆大小限制**:如果应用程序的内存需求超过JVM的最大堆大小设置,也会导致heap space问题。 3. **内存泄漏**:程序中存在未被及时回收的不再使用的对象,长期占用内存资源,最终导致可用堆内存耗尽。 ####...
1.3 Heap堆内存模型 第三节:定位垃圾对象的依据 1.1 引用计数法 1.2 可达性算法 第四节:垃圾回收算法 1.1标记清除算法 1.2复制算法 1.3 标记整理(标记压缩)算法 第五节:垃圾回收器 1.1Serial/Serial Old...
Heapdump-tool工具是专为Java开发者设计的,用于生成和分析堆转储(Heap Dump)文件的强大工具。堆转储文件记录了Java虚拟机(JVM)在某一时刻的内存状态,包括对象、类、垃圾收集器信息等,这对于诊断内存泄漏、...
当内存剩余不到 40 %时,JVM 会增大堆到 Xmx 设置的值,当内存剩余超过 70 %时,JVM 会减小堆到 Xms 设置的值。 垃圾回收 GC 的角色是在 JVM 中调用垃圾回收的机制。GC 的触发机会增加 GC 的触发机会。为了避免...
本文将深入探讨如何在Java中获取JVM内存大小,包括堆内存的总量、最大值以及剩余空间,并解析给定代码片段中的关键概念。 ### JVM内存模型 在讨论如何获取JVM内存大小之前,首先需要理解JVM的内存布局。JVM内存...
IBM Java堆内存分析工具——HeapAnalyzer,是一款专为IBM J9 VM设计的强大内存分析工具,它可以帮助开发者深入理解Java应用程序的内存使用情况,检测并解决内存泄漏问题,从而提升应用性能。本文将详细介绍Heap...
它能帮助开发者深入理解Java虚拟机(JVM)的堆内存状态,通过分析heap dump文件,找出那些占用内存过大的对象,以及这些对象的引用路径,从而定位可能导致问题的代码。 Heap dump是在JVM运行时捕获的一份内存快照,...
这部分在JVM规范中被称为“非堆”(Non-Heap),在Java 8及以后版本,这部分被合并到堆内存中,称为元空间(Metaspace)。 - **本地方法栈(Native Method Stack)**:与JVM栈类似,但服务于本地方法(如C++方法)...