周志明先生所著的《深入理解Java虚拟机:JVM高级特性与最佳实践》(购买地址:亚马逊链接),对我学习Java、理解Java之道有非常大的帮助。至今已读过两遍,为了能够融会贯通,加深记忆(人老了记忆力差),便在Blog上记录一些认为该记的东西。
JVM有自动内存管理机制,简单地说,Java程序员只需要new一个对象,jvm会自动给这个对象分配内存并对内存进行管理,包括当该对象已经“死亡”时对其进行回收。
运行时数据区域
JVM在执行Java程序时会把它所管理的内存划分为若干个不同的数据区域,用于存储不同的数据。根据《Java虚拟机规范(第二版)》(已入手《Java虚拟机规范Java SE 7版》),JVM所管理的内存包括以下几个运行时数据区域,如下图:
程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,用于存储当前执行的字节码指令。
由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,一个处理器在一个确定的时刻,只能执行一条线程的指令。因此,为了程序切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存(虚拟机栈也是线程私有)。
大学时所学的“计算系统基础”,也有“程序计数器”这个概念,但与JVM中的“程序计数器”不同。JVM中的程序计数器是Java程序中线程私有的一块内存区域。“计算系统基础”所说的程序计数器是CPU中的一个寄存器,用来指示电脑正在执行的指令序列。依照特定机器的细节而不同,它可能是保存著正在被执行的指令,也可能是下一个要执行指令的地址。可以参考维基百科或百度百科http://b.baidu.com/view/178145.htm。
Java虚拟机栈
虚拟机栈也是线程私有的,它的生命周期与线程相同。每个方法被执行的时候都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每隔一方法调被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表存放了编译器可知的各种基本数据类型(boolean、byte、char、int等)、对象引用(reference)、和returnAddress(指向了一条字节码指令的地址)。其中64位长度的long和double类型的数据会占用2个局部变量空间(slot),其余的数据类型只占用1个。
本地方法栈
本地方法栈与虚拟机栈的作用是相似的,只是本地方法栈是为虚拟机使用到的Native方法服务。有的虚拟机(如Sun HotSpot)直接把本地方法栈和虚拟机栈合二为一。
Java堆
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。Java堆存放的是对象实例,几乎所有的对象实例以及数组都要在堆上分配。Java堆是垃圾收集器管理的主要区域。
方法区
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器(Just In Time)编译后的代码等数据。相对而言,垃圾收集行为在这个区域时比较少出现的,但并非数据进入了方法区就“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收成绩比较难以令人满意,特别是对类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。
有传言说永久代(PermGen)在JDK8中要被移除了,被“元空间(MetaSpace)”代替:http://caoyaojun1988-163-com.iteye.com/blog/1969853
运行时常量池
运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息时常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
运行时常量池相对于Class文件常量池的一个重要特性是具备动态性,JVm并非只有预置入Class文件中常量池的内容才能进入运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用的比较多的便是String类的intern()方法。
直接内存
直接内存并不是虚拟机运行时数据区的一部分,可以简单地说是除了虚拟机内存外的其他本地物理内存。这些内存在Java中也有可能会被频繁地使用。如NIO引入的基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里的DirectByteBuffer对象作为这块内存的的引用进行操作。
对象访问
不同的虚拟机实现对象访问方式会有所不同,主流的访问方式有两种:使用句柄和直接指针。
使用句柄 如果使用句柄访问方式,Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象示例数据和类型数据各自的具体地址信息。如下图:
使用直接指针 如果使用直接指针访问方式,Java堆对象的布局就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象地址。如下图:
使用句柄的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要被修改。
使用直接指针最大的好处就是快,它节省了一次指针定位的时间开销。HotSpot虚拟机使用的是直接指针进行对象访问。
相关推荐
堆和栈是Java内存管理的两个主要区域,它们各自承担着不同的职责。本笔记将深入探讨这两个区域的工作原理以及如何进行有效的分析。 首先,我们要理解Java内存的两个主要部分:堆(Heap)和栈(Stack)。堆主要用于...
3. **内存溢出与泄漏**: - **内存溢出(Out of Memory Error)**:当JVM无法分配新的内存时,会抛出此错误。可能的原因包括:大对象分配、内存泄漏、堆空间不足等。 - **内存泄漏**:程序中已经不再使用的对象...
- **内存溢出问题**:分析和解决OOM(Out of Memory)问题的方法。 3. **并发编程**: - **线程与进程**:理解线程的创建、同步、通信以及死锁问题。 - **并发工具类**:如Semaphore、CyclicBarrier、...
- Java堆是所有线程共享的一块内存区域,主要用来存放对象实例。 - 包括了所有的对象实例和数组。 - 垃圾收集器的主要管理区域,负责清理不再使用的对象。 - 物理上可以是不连续的内存空间,但在逻辑上被视为...
对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。 3. **弱引用**:比软引用更弱的一种引用关系,弱引用所指向的对象只能生存到下一次垃圾回收发生之前。当...
此外,Java程序内存区域分为堆、栈和方法区,分别存储不同类型的变量。 在面向对象编程中,类(class)是对象(object)的模板,描述了一类事物的共性,而对象则是类的具体实例。Java中的"=="运算符比较内存地址,...
3. **JVM内存模型**: 理解Java内存区域(堆、栈、方法区、本地方法栈、程序计数器),垃圾收集机制(GC算法、GC调优),类加载机制(双亲委派模型)以及内存泄漏和内存溢出的问题。 4. **设计模式**: 掌握常见的23...
方法区也会出现“内存溢出”的异常,即当方法区无法满足新的内存分配需求时,将抛出`OutOfMemoryError`异常。 ##### 线程私有区 - **虚拟机栈**:每个线程拥有一个独立的栈,栈中保存了方法执行的局部变量表、操作...
Java程序运行时,内存分为堆、栈、方法区、程序计数器等区域,分别存储不同类型的对象和数据。 #### 7. 关键字 关键字是Java预定义的具有特定意义的标识符,如`public`, `private`, `static`, `void`, `class`等,...
在JVM运行过程中,可能会遇到各种异常情况,如内存溢出、类加载异常等。理解这些异常的含义和处理方法,对于快速定位和解决问题非常关键。同时,掌握JVM的调试工具,如jstack、jmap、jhat等,可以帮助我们在开发过程...
当系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收后还没有足够的内存,才会抛出内存溢出异常。 3. **弱引用**:弱引用比软引用的生命周期更短。只具有弱引用的对象拥有更...
- **Error**:通常指Java运行环境或虚拟机出错的情况,程序员无法控制也无法恢复,例如内存溢出(Out of Memory Error)等。 - **Exception**:可被程序捕获并处理的异常,进一步细分为: - **运行时异常 ...
- **内存管理**:包括Java内存区域划分(堆、栈、方法区等)、垃圾回收机制(GC)、内存溢出异常(OOM)的分析与解决。 - **多线程**:线程的基本概念、同步机制(synchronized、Lock)、线程池...
3. **内存区域**: - 程序计数器:记录当前线程执行的字节码指令地址。 - Java虚拟机栈:每个方法对应一个栈帧,存储局部变量表、操作数栈、动态链接和方法出口等信息。 - 本地方法栈:与JVM栈类似,但服务于Java...
- **垃圾收集**:GC原理,内存泄漏与内存溢出问题。 以上知识点涵盖了Java的基础到进阶内容,是每个Java程序员必须掌握的核心概念和技术。通过深入学习和实践,可以提升编程能力,解决实际问题。希望这些笔记能助...
12. **JVM内存模型**:理解堆、栈、方法区、程序计数器等区域的划分和作用,以及内存溢出问题的分析和解决。 13. **垃圾回收机制**:了解Java的自动内存管理,包括垃圾收集的原理、算法以及如何调整垃圾回收器。 ...
内存溢出(OutOfMemoryError)是指 JVM 无法分配对象所需内存时抛出的异常。解决方法包括: * 调整堆大小:使用 -Xms 和 -Xmx 选项调整堆的初始大小和最大大小。 * 找出无法被回收的大对象:使用 Eclipse MAT 分析...
垃圾收集器会在适当的时候回收这些对象,确保内存高效利用,防止内存溢出异常。 内存溢出(Memory Overflow)发生于分配给程序的内存不足以存储所需的数据,而内存泄漏(Memory Leak)则指对象在使用后未被正确释放...
4. 内存溢出:了解各种内存溢出异常,如OOM(Out Of Memory),并针对性地解决。 六、JVM监控与诊断工具 JDK提供了一些工具帮助开发者监控和诊断JVM,如JConsole、VisualVM和JProfiler,它们可以显示JVM内存状态、...
- Java堆是JVM中最大的一块内存区域,主要用于存放对象实例。所有线程共享Java堆。 - 垃圾收集器主要管理Java堆,因此它也被称为“GC堆”。堆内存可动态扩展,扩展失败会导致`OutOfMemoryError`异常。 3. **...