JAVA运行时数据区域
1.程序计数器
程序计数器(Program Counter Register) 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条执行字节码指令。
每条线程都有一个独立的程序计数器。这类内存也被称为“线程私有内存”。
如果执行的是java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址。如果是native方法,计数器为空。此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2.java虚拟机栈
同样是线程私有,描述Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。一个方法对应一个栈帧。
局部变量表存放了各种基本类型、对象引用和returnAddress类型(指向了一条字节码指令地址)。其中64位长度long 和 double占两个局部变量空间,其他只占一个。
规定的异常情况有两种:1.线程请求的栈的深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;2.如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就抛出OutOfMemoryError异常。
3.本地方法栈
和Java虚拟机栈很类似,不同的是本地方法栈为Native方法服务。也会抛出StackOverflowError和OutOfMemoryError。
4.java堆
堆是Java虚拟机所管理的内存中最大的一块。由所有线程共享,在虚拟机启动时创建。堆区唯一目的就是存放对象实例。
堆中可细分为新生代和老年代,新生代再细分可分为Eden空间、From Survivor空间、To Survivor空间。
堆无法扩展时,抛出OutOfMemoryError异常。
下面以hotspot的堆内存做个示例:
堆大小可以通过-Xms,-Xmx来指定。
堆主要分为两个区域:新生代(主要用来存放新生的对象)和老年代(主要存放长生命周期的对象)。新生代和老年代的比例为1:2,可以通过-XX:NewRatio来指定。
新生代又分为三个区域:Eden、From Survivor、To Survivor。三个区域的比例为8:1:1,JVM每次都只会用使用Eden和一块Survivor来存储对象。
Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。
Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。
但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制(新生代采用复制算法收集内存)。
为了能够更好的适应不同的程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。 Full GC 是发生在老年代的垃圾收集动作,所采用的是“标记-清除”或者“标记-整理”算法。
现实的生活中,老年代的人通常会比新生代的人 “早死”。堆内存中的老年代(Old)不同于这个,老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。
在发生MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么MinorGC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试这进行一次MinorGC,尽管这次MinorGC是有风险的;如果小于,或者HandlePromptionFailure设置不允许冒险,那这是也要改为进行一次FullGC.
另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。
堆设置
-Xms :初始堆大小
-Xmx :最大堆大小
-Xmn:新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-XX:NewSize=n :设置年轻代大小
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:PermSize=n 永久代(方法区)的初始大小
-XX:MaxPermSize=n :设置永久代大小
-Xss 设定栈容量;对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,因为在HotSpot中并不区分虚拟机和本地方法栈。
-XX:PretenureSizeThreshold (该设置只对Serial和ParNew收集器生效) 可以设置进入老生代的大小限制
-XX:MaxTenuringThreshold=1(默认15)垃圾最大年龄 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率,该参数只有在串行GC时才有效
5.方法区
所有线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
当方法区无法满足内存分配需求时,抛出OutOfMemoryError。
永久代的垃圾收集主要回收两部分内容:废弃的常量和无用的类。
废弃的常量:回收废弃常量与回收java堆中的对象非常类似。以常量池字面量的回收为例,加入一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做”abc”的,换句话说,就是有任何String对象应用常量池中的”abc”常量,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个“abc”常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。(注:jdk1.7及其之后的版本已经将字符串常量池从永久代中移出)
无用的类:类需要同时满足下面3个条件才能算是“无用的类”:
- 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
- 加载该类的ClassLoader已经被回收
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是”可以“,而并不和对象一样,不使用了就必然会回收。是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc(关闭CLASS的垃圾回收功能,就是虚拟机加载的类,即便是不使用,没有实例也不会回收。)参数进行控制。
6.运行时常量池
是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项是常量池(Const Pool Table),用于存放编译期生成的各种字面量和符号引用。并非预置入Class文件中常量池的内容才进入方法运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
当方法区无法满足内存分配需求时,抛出OutOfMemoryError。
7.直接内存
并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。
JDK1.4加入了NIO,引入一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。因为避免了在Java堆和Native堆中来回复制数据,提高了性能。
当各个内存区域总和大于物理内存限制,抛出OutOfMemoryError异常。
相关推荐
本文将围绕JVM运行时数据区域进行详细介绍,包括程序计数器、Java虚拟机栈、本地方法栈、方法区以及Java堆等组成部分。 #### 二、程序计数器 **定义**:程序计数器是一块较小的内存空间,它可以被视为当前线程所...
综上所述,Java运行时内存分配涉及多个不同的存储区域,每个区域都有其特点和适用场景。理解这些区域及其用途有助于开发者更好地优化代码性能,避免常见的内存泄漏问题。例如,合理地选择对象的存储位置可以显著提高...
Java虚拟机运行时数据区分析 Java虚拟机(JVM)是一种抽象的计算机,它提供了一个运行Java字节码的环境。JVM将Java源代码编译为字节码,并在运行时执行这些字节码。为了更好地理解JVM的工作原理,我们需要了解JVM的...
在深入学习Java编程语言的过程中,理解Java虚拟机(JVM)的运行时数据区域是至关重要的。JVM的运行时数据区是程序运行时存储数据的地方,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆以及方法区。以下是这些...
Java运行时监控是Java开发和运维过程中至关重要的一个环节,它可以帮助我们理解应用程序在实际运行中的性能、资源消耗以及可能出现的问题。通过对Java应用的实时监控,我们可以及时发现并解决潜在的性能瓶颈,优化...
Java运行时数据区划分原理是Java虚拟机中的一种内存管理机制,它将内存区域划分为不同的部分,每个部分都有其特定的作用和功能。了解Java运行时数据区划分原理对于Java程序员来说非常重要,因为它可以帮助他们更好地...
Java虚拟机(JVM)的核心组件之一便是其运行时数据区域,这一区域负责存储程序运行过程中产生的各种数据。为了更好地理解这部分内容,我们将深入探讨运行时数据区域内的各个组成部分。 ##### 1.1 PC 寄存器 (Program ...
文章通过在特定测试区域应用该方法,处理LiDAR点云数据,并与在不同语言环境下处理的结果进行了比较,验证了方法的可行性及Java语言环境下的实现能力。研究结果表明,将网格索引与树结构索引结合的方法能够有效地...
元数据在Java中扮演着至关重要的角色,特别是在使用JDBC(Java Database Connectivity)连接和操作数据库时。元数据是关于数据的数据,它提供了关于数据仓库、数据库对象以及它们如何相互作用的关键信息。对于Java...
这要求Java代码具有高效的时间复杂度和内存管理能力,以确保在处理大量数据时仍能保持良好的运行性能。 数据库技术在这里也扮演了重要角色。MySQL,作为标签中提及的一种关系型数据库管理系统,被用来存储历史天气...
总结起来,"Java 获取区域号段"涉及从数据源获取并处理区域号段信息,然后在Java程序中实现查询功能。这可能需要用到第三方库,如Google的libphonenumber,以及处理和存储数据的技巧。提供的JAR文件可能包含实现这些...
03 JVM 运行时数据区概述及线程的 PPT 重绘。讲述 Java 虚拟机 运行时数据区所处位置,结构划分,以及各个区域与线程的关系。
当需要对这些数据进行复杂的几何操作,如缓冲区分析、拓扑检查或者创建具有特定属性的区域时,就需要将栅格数据转化为矢量数据,也就是面数据集。在本范例中,这个过程涉及到两个关键步骤: 1. **象元融合**:对象...
Java编程语言的基础知识中,数据...总结来说,Java的数据类型提供了基础的值存储结构,而运行时常量池则是JVM内存模型中用于存储类和接口常量的动态区域。理解这两个概念对于深入学习Java编程和优化代码性能至关重要。
Java运行时数据区是Java虚拟机(JVM)在执行Java程序时管理内存的核心组成部分。这一数据区由多个不同的内存区域组成,每个区域都有特定的用途,为程序的执行提供必要的支持。以下是对这些区域的详细说明: 1. **...
Java虚拟机(JVM)作为Java程序的运行环境,定义了一系列用于程序执行过程中使用的数据区域。这些数据区域在JVM启动时创建,在JVM退出时销毁。其中一些数据区域是线程独立的,即每个线程都有自己的数据区域;而另...
**Java运行内存详解** 在Java编程环境中,理解Java运行内存是非常关键的,它涉及到程序的性能优化和问题排查。Java虚拟机(JVM)为Java应用程序提供了运行时环境,其中包括了多个内存区域,这些区域各自负责不同的...
【Java运行时数据区】是Java虚拟机(JVM)在执行Java程序时的重要组成部分,它根据JVM的体系结构划分为多个区域,每个区域都有特定的用途和生命周期。了解这些区域对于优化程序性能和避免内存泄漏至关重要。 1. **...