洮南网站建设哪家好,wordpress取消邮件验证码,求个网站你明白的,优设网网站设计评价目录
概述
堆空间内部结构
JDK7版本
JDK8版本
堆空间的内存划分
堆空间大小设置参数 概述 Java堆是虚拟机所管理的内存中最大的一块#xff0c;其在JVM启动时即被创建#xff0c;并且空间大小也被确定#xff08;这里是不考虑Java8之后以本地内存来实现的元空间其在JVM启动时即被创建并且空间大小也被确定这里是不考虑Java8之后以本地内存来实现的元空间在Java8之前可以很确切的这么说个人认为如果将其与元空间比较意义并不大同时也是所有线程共享的一块内存区域此内存区域的唯一目的就是存放对象实例”几乎“所有的对象实例都在这里分配注意这里的“几乎”二字除了堆上分配还可以栈上分配这依赖于逃逸分析技术的提高。 堆内存被分为了新生代Young、老年代Old新生代又划分为伊甸区Eden幸存者0区Survivor 0和幸存者1区Survivor 1有的资料也写作from 区和 to 区。 从Java堆的内存划分便可以体现出Java堆是内存回收的重点地区这种划分形式也是为了更高效的实现垃圾回收。 堆空间内部结构 我们平常讨论的JVM如果没有特别指明类型的话默认都是以HotSpot虚拟机而言下文也是入此。 说到堆空间的内部结构需要根据JDK的版本而定在JDK7及之前HotSpot虚拟机选择将方法区和堆空间合并实现称作永久代其好处就是省略了专门为方法区编写代码可以复用堆空间的代码。但是以现在的眼光来看还是存在诸多弊端。所以在JDK8改为用本地内存来实现方法区称作元空间。 JDK7版本
方法区被叫做永久代并且和堆空间合并一起实现。其实在JDK7时开发人员就已经意识到了缺陷并且在开始解决了JDK7时将字符串常量池、静态变量从永久代中存放到堆空间中。 JDK8版本
移除永久代用本地内存实现方法区改称元空间。其中原本存放在永久代中的类型信息、字段、方法、常量保存在本地内存的元空间字符串常量池和静态变量依旧保存在堆中堆是内存回收的重点区域而字符串是开发中经常用到的所以也需要频繁的清理所以字符串常量池放在堆空间是合理的。 堆空间的内存划分
堆空间被分为了新生代、老年代新生代被划分为伊甸区、幸存者0区、幸存者1区。
堆内存默认结构占比 新生代:老年代 1:2 伊甸区:幸存者0区:幸存者1区 8:1:1 堆空间大小设置参数
Java堆区用于存储Java对象实例堆的大小在JVM启动时就已经设定好了可以在启动时设置启动参数来指定堆空间的大小。 “-Xms用于表示堆区的起始内存等价于-XX:InitialHeapSize“-Xmx则用于表示堆区的最大内存等价于-XX:MaxHeapSize一旦堆区中的内存大小超过“-Xmx所指定的最大内存时将会抛出OutOfMemoryError异常。
通常会将-Xms和-Xmx两个参数配置相同的值其目的是为了能够在ava垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小从而提高性能。
堆空间分代思想
上文已经提到了默认情况下堆空间的分代结构以及具体的内存大小划分。这种分代形式主要跟垃圾回收机制有关垃圾回收机制大多数都遵循了分代收集的原则 弱分代假说绝大多数对象都是朝生熄灭的。强分代假说熬过越多次垃圾收集的对象就越难以消亡。跨代引用假说跨代引用相对于同代引用来说仅占极少数。可以粗略的理解为存放在堆空间的对象需要经常清理但是对象与对象之间的存活周期也是不同的。那些存活周期比较长的对象存放在老年代中朝生熄灭的对象存放在新生代中。可以通过设置对两个代的垃圾扫描频率来达到更高的回收效率新生代的对象朝生熄灭那么就需要经常清理老年代的存活周期长可以降低频率提高效率。 分代内存设置
设置新生代内存大小 “-XX:NewSize” 新生代的最小值 ”-XX:MaxNewSize“ 新生代的最大值 “-XX:NewRatio” 设置新生代与老年代在堆空间的大小 -XX:SurvivorRatio 设置幸存者区和伊甸区的大小比值注意幸存者区有两个 例1-XX:NewSize10M -XX:MaxNewSize20M 设置新生代最小值为10M最大值为20M。
例2-XX:NewRatio4 设置新生代和老年代的比值为4即 新生代 老年代 1 4
例3-XX:SurvivorRatio8 表示 幸存者区和伊甸区的大小比例为 1 8。
对象在堆中的分配过程 上面的流程图表示的是对象在堆中内存分配的过程内存分配和内存回收密切相关。 创建的对象先放伊甸园区此区有大小限制。当伊甸园的空间填满时程序又需要创建对象JVM的垃圾回收器将对伊甸园区进行垃圾回收MinorGC将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区。值得注意的是幸存者区空间填满时并不会触发垃圾回收对幸存者区的回收是在MinorGC中顺便做的事情然后将伊甸园中的剩余对象移动到幸存者0区。如果再次触发垃圾回收此时上次幸存下来的被放到幸存者0区的对象如果没有被回收就会放到幸存者1区。幸存者两个区之间是通过标记复制算法进行垃圾回收的如果再次经历垃圾回收此时会重新放回幸存者0区接着再去幸存者1区。对象什么时候能够去到老年代JVM默认是经历了15次垃圾回收之后。当然也可以设置参数来控制设置 -Xx:MaxTenuringThresholdN当老年代内存不足时再次触发GCMajor GC进行养老区的内存清理。若老年代执行了Major GC之后发现依然无法进行对象的保存就会产生OOM异常。下图主要描述的是幸存者区之间的关系。 上面的描述只是从宏观的角度描述了对象在堆中的内存分配接下来仔细考虑以下几个问题 具体的分配方式是什么 对于第一个问题首先需要清楚的是对象所需的内存在类加载阶段完成后便可确定下来。为对象分配内存实际上就是将一块内存从java堆中划分出来存放这个对象。如果java堆中的内存是绝对规整的即所有用过的内存放置在一边所有没用过的内存放置在另一边。那么我们可以很方便的使用一个中间指针来分配内存新的对象到来了中间指针就往空闲内存方向移动新的对象的内存大小的距离即可。这种分配方式称为“指针碰撞”Bump The Pointer。但是如果java堆中的内存并不是规整的已使用过的和未使用过的内存交织在一起就不能使用指针碰撞的方式分配内存了虚拟机就需要维护一个列表记录哪些内存块是可用的分配时从列表中找到一块足够大的空间划分给对象即可并更新列表上的记录这种分配方式称为“空闲列表”Free List。选择哪种分配方式取决于java堆是否规整而java堆是否规整又取决于所采用的垃圾收集器是否带有空间压缩整理的能力决定。 对象的分配是很频繁的操作并发情况下是线程安全的吗 对于第二个问题对象创建时并发情况下可能出现正准备分配给A对象的内存在指针还没来得及分配时B对象又使用了这一块内存区域的情况。解决这个问题有两种可选方案第一种就是对分配内存空间的动作进行同步处理虚拟机采用的是CAS配上失败重试的方式来保证更新操作的原子性。另一种方式是把内存分配的动作按照线程划分在不同的空间中进行给每一个线程在java堆中预先分配一小块内存称为本地线程分配缓冲线程要在堆中分配内存首先在各自的本地缓冲区中分配本地缓冲区用完了分配新的缓冲区时才需要同步锁定。