织梦网站如何修改数据库配置,企业门户app,crm管理系统单机版,上海网页制作机构JVM内存管理涉及但不限于类加载、对象分配、垃圾回收等#xff0c;本篇主要记录运行时数据区域与对象相关内容。 内容主要来源《深入理解Java虚拟机#xff1a;JVM高级特性与最佳实践》与官方文档#xff0c;理解与表述错漏之处恳请各位大佬指正。 目录 运行时数据区域 栈 栈…JVM内存管理涉及但不限于类加载、对象分配、垃圾回收等本篇主要记录运行时数据区域与对象相关内容。 内容主要来源《深入理解Java虚拟机JVM高级特性与最佳实践》与官方文档理解与表述错漏之处恳请各位大佬指正。 目录 运行时数据区域 栈 栈帧 栈异常 栈内存大小配置 配置参数 堆 配置参数 字符串常量池注意字符串常量池自jdk8起在堆 静态变量特别说明 方法区 运行时常量池 常量说明 常量分析实例 配置参数 程序计数器 直接内存 总结 对象 对象创建 内存分配更新操作的原子性原理 对象访问 内存泄漏分析生产 大对象 大对象的影响 解决 运行时数据区域
内存管理基于内存动态分配与垃圾收集技术。 编译期间做的事情:给变量分配空间 栈
一般讲的都是虚拟机栈更多情况下指虚拟机栈中局部变量表部分。本地方法栈执行的都是本地的Native方法。 栈帧
每个线程都有自己的虚拟机栈而方法作为执行的基本单元在 每个方法执行的时候都会创建一个栈帧Stack Frame用于存储局部变量表、操作数栈、动态连接、方法出口信息等。 其中 局部变量表存放了编译期可知的 基本数据类型、对象引用。 了解就行栈帧中存储的具体信息包括 方法参数包括传递给方法的实际参数以及方法的隐式参数如 this 指针。局部变量定义在方法中的变量包括基本类型和对象引用。操作数栈用于存储方法执行过程中产生的临时数值如算术运算的结果、方法调用的返回值等。方法返回地址指向方法调用者应该返回的下一条指令的地址。异常处理器存储方法对应的异常处理器的信息用于处理方法执行中可能发生的异常。其它包括方法的访问标志、常量池引用等。 栈异常
StackOverflowError 栈溢出 OutOfMemoryError 对于栈的提示是 java.lang.OutOfMemoryError: unable to create new native thread。此时无法创建新线程 栈内存大小配置
HotSpot虚拟机的栈容量不可动态拓展在配置JVM时通过-参数Xss256k配置。 JVM的总内存大小会影响线程栈内存的可用量。如果堆内存或者其他内存区域占用过多剩余内存可能不足以支持大量线程。 配置参数
-Xss栈大小设置在64位Linux系统上栈大小默认1M 堆 堆是垃圾回收管理器管理的区域储物理上可以不连续逻辑上是连续的内存空间。因垃圾收集器基于分代收集理论设计所以将 堆分代划分(方便回收和分配内存)。 “新生代、老年代、Eden空间、From Survivor空间、To Survivor空间”。 堆1:2新生代:老年代, 其中新生代8:1:1 eden:from:to 存放几乎所有对象实例与数组基本类型数组与对象类型数组、字符串常量、静态变量。 配置参数
-Xms堆内存起始大小 -Xmx最大堆内存 字符串常量池注意字符串常量池自jdk8起在堆
专门用于存储字符串字面量的一个特殊区域。它的设计目的是通过缓存机制避免重复创建相同的字符串对象从而节省内存并提高性能。 值得注意的是HotSpotJVM自JDK8将字符串常量池实现为堆中的一部分从方法区移出以优化内存管理并减少方法区的压力。 资料来源 JEP 122: Remove the Permanent Generation Chapter 5. Loading, Linking, and Initializing 静态变量特别说明
有资料说在方法区、有资料说在堆给我整不会了。直接看官网 JEP 122: Remove the Permanent Generation 结论静态变量引用或基本类型值静态变量在元空间对象实例包括静态对象的值在堆中 方法区 方法区也是线程共享的内存区域主要用于存储已被虚拟机加载的 类型信息、静态变量对象的引用与基本类型静态变量、即时编译器编译后的代码缓存等数据。是基于本地内存实现的方法区内存回收主要目标是常量池的回收和对类型的卸载 运行时常量池 运行时常量池是方法区的一部分是常量池表Class文件常量池运行时的表示形式其中常量池表存储的是 符号引用Symbolic References和 字面量Literal Values而 不是实际的数据存储位置。它的核心作用是 存储类、方法、字段的全限定名描述保存方法描述符Descriptor存储字符串常量字面量记录数值型常量的原始值 常量说明
在代码中使用final声明的常量会放到方法区运行时常量池 方法内变量临时变量存储在虚拟机栈中 常量分析实例 配置参数
-XX:MetaspaceSize初始触发 GC 的阈值默认约 21MB。 -XX:MaxMetaspaceSize最大大小默认无上限受本地内存限制。 -XX:MinMetaspaceFreeRatio 和 -XX:MaxMetaspaceFreeRatio控制 GC 后空闲比例默认 40% 和 70%。 程序计数器
作为游标指示字节码命令执行每个线程的程序计数器是独立的。 计数器的值是正在执行的虚拟机字节码指令的地址。 直接内存
直接内存是指Java应用程序在JVM堆之外直接向操作系统申请并管理的一段内存区域。堆外内存的一种具体实现形式。 使用Native函数分配的堆外内存用与NIO等操作。 设置虚拟机内存时需要小于物理内存给直接内存留空间。因为运行时JVM内存设置的内存直接内存实际占用的比设置的要大。防止动态扩展时出现OutOfMemoryError异常。 堆外内存通过DirectByteBuffer进行操作避免在Java堆中和Native堆中来回复制数据。 需要注意的就是物理内存应该jvm内存 总结 对象
对象可以划分为对象头Header、实例数据(Instance)、对齐填充Padding. 对象头包括两类信息。一部分包括对象自行运行时数据哈 希码HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等这部 分数据的长度在32位和64位的虚拟机未开启压缩指针中分别为32个比特和64个比特官方称它 为“Mark Word”。 另一部分是类型指针用于JVM确认对象所属类实例。 对象创建
JVM执行new指令时会区常量池定位这个类的符号引用检查这个符号引用代表的类是否已经被加载、解析、初始化过没有则进行类加载。 为新对象分配内存的方式大致分为两种 堆为对象分配内存的方式 “指针碰撞”——假设Java堆中内存是绝对规整的所有被使用过的内存都被放在一 边空闲的内存被放在另一边中间放着一个指针作为分界点的指示器那所分配内存就仅仅是把那 个指针向空闲空间方向挪动一段与对象大小相等的距离。空闲列表——假设Java堆中的内存并不是规整的已被使用的内存和空闲的内存相互交错在一起那 就没有办法简单地进行指针碰撞了虚拟机就必须维护一个列表记录上哪些内存块是可用的在分 配的时候从列表中找到一块足够大的空间划分给对象实例并更新列表上的记录。 JVM选择哪种依据其垃圾收集器决定。 内存分配更新操作的原子性原理
划分空间时在并发情况下可能是不安全的。例如对象A与对象B同时使用指针分配内存。 JVM默认采用CASCompare And Swap搭配失败重试的方式保证操作的原子性。 对象访问
HotSpot主要使用直接指针访问对象。 内存泄漏分析生产 JVM配置参数-XX:HeapDumpOnOutOfMemoryError 可以让虚拟机在出现内存溢出异常的时候Dump当前内存堆转储快照。 也可以使用JMap命令导出Dump。 然后使用mat工具进行分析如下 大对象
占用较大连续内存空间的对象一般是大数组、包含大量字段或嵌套对象的对象复杂对象结构、大型字符串对象。 将大对象直接分配到老年代可以避免“占用新生代内存导致提前GC带来的性能损耗”、“大对象在垃圾回收时的复制带来的性能损耗”是一种JVM优化策略。 值得注意的是JDK8默认收集器是Parallel Scavenge与Parallel Old大对象不会直接放到老年代。会照常进行回收流程。 JVM在配置使用Serial和parNew两款收集器时可以通过参数-XX:PretenureSizeThreshold来设定一个阈值如果对象的大小超过阈值会被视为“大对象”并被直接分配到老年代。 对于G1收集器其引入了“HUmongous对象”的概念任何超过Region大小一半的对象都会被视为巨型对象直接分配到老年代。 大对象的影响 内存分配大对象可能需要分配较大的连续内存块这可能导致堆内存碎片化问题。垃圾回收某些垃圾回收器如 G1 GC对大对象有特殊处理机制可能会直接分配到老年代而不是新生代。 解决
在日常编码中要尽量避免大对象不可避免时尽量复用如使用对象池。 对于大数组对象进行分割、将其分割为小对象进行操作。 或者使用手动释放的堆外内存。 try { ByteBuffer buffer ByteBuffer.allocateDirect(1024 * 1024); // 使用 buffer } finally { if (buffer ! null buffer.isDirect()) { ((sun.misc.Cleaner) sun.misc.Unsafe.getUnsafe().invokeCleaner(buffer)).clean(); } }