- :设置永久代的初始大小;
-
设置永久代的最大值,默认64M。
永久代在启动时就已确定大小,空间配置有限,容易出现报错。难以进行调优,只有FGC时会移动类元信息,因为它比新生代和老年代拥有更长的生命周期,永久代存在频率较低的GC。
运行时常量池
Runtime Constant Pool,方法区的一部分,class文件除有类的版本、字段、方法接口等描述信息外,还有一项信息是常量池,用于存储编译期生成的各种,这部分内容将在类加载后进入方法区运行时常量池中存放。此区域同样会发生OOM异常。
class文件中的常量池,也称为静态常量池,JVM虚拟机完成类装载操作后,会把静态常量池加载到内存中,存放在运行时常量池。
Heap,堆是用来存放几乎所有对象实例和数组的内存空间,堆的内存空间是可自定义大小的,支持运行时动态修改,通过、两个参数调整堆的初始值和最大值。即memory start,代表最小堆容量,即memory max,代表最大堆容量。包括TLAB。
特点:
- 线程共享:整个JVM内只有一个堆,所有线程都访问同一个堆。程序计数器、Java虚拟机栈、本地方法栈都是线程私有。
- 在虚拟机启动时创建;
- 垃圾回收的主要场所;
- 可进一步细分为:新生代、老年代;新生代又可被分为:Eden、From Survior、To Survior。不同区域存放具有不同生命周期的对象。这样可根据不同区域使用不同的GC算法;
- 堆的大小既可固定也可扩展,当线程请求分配内存,但堆已满,且内存已满无法再扩展时,抛出OOM;
- 堆是逻辑上连续不间断,物理上可以不连续的内存空间

面试问题:对象一定分配在堆中吗?
不一定,JVM通过逃逸分析,那些逃不出方法的对象会在栈上分配。
逃逸分析:Escape Analysis,一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象引用的使用范围,从而决定是否要将这个对象分配到堆上。仅存在于server模式的JDK。
在编译期间,JIT会对代码做很多优化,目的是减少内存堆分配压力。
一种确定指针动态范围的静态分析,它可分析在程序的哪些地方可访问到指针;同编译器优化原理的指针分析和外形分析相关联。在JVM的即时编译语境下,逃逸分析将判断新建的对象是否逃逸。即时编译判断对象是否逃逸的依据:一种是对象是否被存入堆中(静态字段或堆中对象的实例字段),另一种就是对象是否被传入未知代码。
当变量(或对象)在方法中分配后,其指针有可能被返回或被全局引用,这样就会被其他方法或线程所引用,这种现象称作指针(引用)的逃逸。
局部对象user未被外部调用:
局部对象user可能会被外部调用:

- 对象栈上分配:如果确定一个对象不会逃逸到线程之外,就可考虑将这个对象在栈上分配(栈分配可快速地在栈帧上创建和销毁对象),而不是分配到堆空间,对象占用的内存随着栈帧出栈而被销毁,降低GC压力;
- 同步锁消除:线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个对象不会逃逸出线程,无法被其他线程访问(只能从一个线程访问),则这个变量的读写肯定不会有竞争,对这个变量实施的同步措施也就可安全地消除掉,即synchronized同步锁。JVM并不能消除Lock锁。要开启同步消除,需加上参数。因为这个参数依赖逃逸分析,所以同时要打开选项。另外,参数可打印锁消除信息。
- 分离对象标量替换:如果一个数据是不可拆分的基本数据类型,则称之为。把一个Java对象拆散,将其用到的成员变量恢复为原始类型来访问,这个过程就称为。假如逃逸分析能够证明一个对象不会被方法外部访问,且可被拆散,则可以不创建对象,用创建若干个基本类型的成员变量来代替,让对象的成员变量在栈上分配和读写。不用生成对象头减少内存使用;降低GC频率。
示例代码:
如上代码中对lock对象进行加锁,如果lock对象的生命周期只在方法中,并不会被其他线程所访问到,则在JIT编译阶段会被优化成:
内存分配有两种方式:
- 指针碰撞:Bump The Pointer,假设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,分配内存仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的实例;
- 空闲列表:Free List,如果Java堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,虚拟机必须维护一个列表,记录哪些内存块可用,在分配时从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
选择使用哪种方式,由Java堆是否规整决定,更进一步,由垃圾收集器是否具有压缩整理能力决定。
对象创建在虚拟机中是非常频繁的行为,可能存在线性安全问题:如果一个线程正在给A对象分配内存,指针还没来得及修改,同时另一个为B对象分配内存的线程,仍引用着之前的指针指向,即发生抢占。
有两种方案:
- 采用CAS分配重试方式来保证更新指针操作的原子性;
- TLAB,先在TLAB中分配,TLAB用完才需要同步锁定。
TLAB
Thread Local Allocation Buffer,线程本地分配缓冲。把内存分配的动作按照线程划分在不同的空间之中进行,每个线程在Java堆中预先分配一小块内存,即TLAB,位于年轻代Eden区。在堆上分配内存需锁定整个堆,在TLAB上不需要进行同步锁定,JVM在分配对象时会尽量在TLAB上分配,以提高效率。
能减少多线程下并发创建对象造成的性能下降,可通过设定:


站在垃圾收集器的角度来看,可把内存分为新生代与老年代。内存的分配规则取决于当前使用的GC组合,以及内存相关的参数配置。对象优先分配在新生代Eden区,而大对象直接进入老年代。
第一,对象优先分配在新生代Eden区,JVM为每个线程分配一个私有TLAB,其结构如下:

第三、老年代。老年代放置长生命周期的对象,通常是从Survivor区域拷贝过来的对象,不过当对象过大的时候,无法在新生代中用连续内存的存放,那么这个大对象就会被直接分配在老年代上。一般来说,普通的对象都是分配在TLAB上,较大的对象,直接分配在Eden区上的其他内存区域,而过大的对象,直接分配在老年代上。
第四、Vritual空间。可以使用Xms与Xmx来指定堆的最小与最大空间。如果Xms小于Xmx,堆的大小不会直接扩展到上限,而是留着一部分等待内存需求不断增长时,再分配给新生代。Vritual空间便是这部分保留的内存区域。

Direct Memory,不是虚拟机运行时数据区的一部分,不由JVM管理,堆外内存。内存分配不受Java堆大小的限制但受整个内存大小的限制。
在NIO里,JVM通过堆上的DirectByteBuffer操作直接内存,利用本地方法库直接在Java堆之外申请的内存区域。
好处:避免在Java堆和native堆之间同步数据的步骤。
字节码缓存。Non-heap区域用于存储由JIT编译期生成的编译后代码。由内存直接分配;由Code Cache清理器管理。


参数的作用,当从32位虚拟机迁移到64位虚拟机上,JVM会将部分指针进行压缩,防止在64位系统中占用更大内存。
当发生YGC时,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果这个条件成立,那么YGC可以确保是安全的。当大量对象在YGC后仍然存活,就需要老年代进行分空间分配担保,把Survivor无法容纳的对象直接进入老年代。如果老年代判断剩余空间不足(根据以往每一次回收晋升到老年代对象容量的平均值作为判定值)进行FGC。
内存是由若干个黑色的内存颗粒构成的。每一个内存颗粒叫做一个chip。每个chip内部,是由8个bank组成的。bank是一个二维矩阵,矩阵中每一个元素中都保存1个字节,即8个bit。
- 堆内存。最常见,如果在堆中没有内存完成对象实例的分配,并且堆无法再扩展时,将抛出OOM异常。可以通过-Xmx和-Xms来控制堆内存的大小,发生堆上OOM的可能是存在内存泄露,也可能是堆大小分配不合理。
- Java虚拟机栈和本地方法栈,这两个区域的区别不过是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务,在内存分配异常上是相同的。对Java虚拟机栈规定两种异常:
- 如果线程请求的栈大于所分配的栈大小,则抛出SOF错误,比如一个不会停止的递归调用;
- 如果虚拟机栈是可动态拓展,拓展时无法申请到足够的内存,则抛出OOM错误。
- 直接内存。直接内存虽然不是虚拟机运行时数据区的一部分,但既然是内存,就会受到物理内存的限制。NIO使用Native函数库在堆外内存上直接分配内存,但直接内存不足时也会导致OOM。
- 方法区。随着Metaspace元数据区的引入,方法区的OOM错误信息也变成。
对于旧版本的Oracle JDK,永久代大小有限,而JVM对永久代的垃圾回收并不积极,如果往永久代不断写入数据,例如调用,在永久代占用太多空间导致内存不足,也会出现OOM的问题,错误信息为
SOF表示当前线程申请的栈超过事先定好的栈的最大深度,但内存空间可能还有很多。OOM是指当线程申请栈时发现栈已满,且内存也全都用光。
查看堆内存的工具,两类:图形化工具和命令行工具。
- 图形化工具:直观,连接到Java进程后,可以显示堆内存、堆外内存的使用情况。如:JConsole,VisualVM、MAT等。
- 命令行工具:可在运行时进行查询,包括jstat,jmap等,可对堆内存、方法区等进行查看。用于定位线上问题。jmap也可以生成堆转储文件(Heap Dump)文件,如果是在Linux上,可以将堆转储文件拉到本地来,使用Eclipse MAT进行分析,也可使用jhap进行分析。
- JVM经典五十问
- JVM内存布局详解
jvm内存模型(JVM内存模型五大模型图解)
jvm内存模型(JVM内存模型五大模型图解)设置永久代的初始大小 设置永久代的最大值 默认 64M 永久代在启动时就已确定大小 空间配置有限 容易出现报错 难以进行调优 只有 FGC 时会移动类元信息 因为它比新生代和老年代拥有更长的生命周期 永久代存在频率较低的 GC 元空间 方法区存在于元空间 物理内存不再与堆连续 而是直接存在于本地内存中 理论上机器 运行时常量池 Runtime Constant Pool 方法区的一部分
大家好,我是讯享网,很高兴认识大家。
2025年sql数据库文件在哪(sql数据库文件在哪里找)
上一篇
2025-04-14 10:16
2025年计算机硬件基础书(计算机硬件基础书电子版)
下一篇
2025-05-23 17:25









版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/203635.html