Skip to content

Commit 3d6e429

Browse files
committed
Update Java内存区域.md
1 parent 66c2feb commit 3d6e429

File tree

1 file changed

+40
-7
lines changed

1 file changed

+40
-7
lines changed

docs/java/jvm/Java内存区域.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,48 @@ Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共
136136

137137
Java 堆是垃圾收集器管理的主要区域,因此也被称作**GC 堆(Garbage Collected Heap)**.从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。**进一步划分的目的是更好地回收内存,或者更快地分配内存。**
138138

139-
<div align="center">
140-
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3堆结构.png" width="400px"/>
141-
</div>
142-
143-
上图所示的 eden 区、s0 区、s1 区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 `-XX:MaxTenuringThreshold` 来设置。
139+
在 JDK 7 版本及JDK 7 版本之前,堆内存被通常被分为下面三部分:
140+
141+
1. 新生代内存(Young Ceneration)
142+
2. 老生代(Old Generation)
143+
3. 永生代(Permanent Generation)
144+
145+
![JVM堆内存结构-JDK7](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/JVM堆内存结构-JDK7.jpg)
146+
147+
JDK 8 版本之后方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。
148+
149+
![JVM堆内存结构-JDK8](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/JVM堆内存结构-jdk8.jpg)
150+
151+
**上图所示的 Eden 区、两个 Survivor 区都属于新生代(为了区分,这两个 Survivor 区域按照顺序被命名为 from 和 to),中间一层属于老年代。**
152+
153+
大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 `-XX:MaxTenuringThreshold` 来设置。
154+
155+
> 修正([issue552](https://github.com/Snailclimb/JavaGuide/issues/552)):“Hotspot遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的晋升年龄阈值”。
156+
>
157+
> **动态年龄计算的代码如下**
158+
>
159+
> ```c++
160+
> uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
161+
> //survivor_capacity是survivor空间的大小
162+
> size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
163+
> size_t total = 0;
164+
> uint age = 1;
165+
> while (age < table_size) {
166+
> total += sizes[age];//sizes数组是每个年龄段对象大小
167+
> if (total > desired_survivor_size) break;
168+
> age++;
169+
> }
170+
> uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
171+
> ...
172+
> }
173+
>
174+
> ```
175+
>
176+
>
144177
145178
### 2.5 方法区
146179
147-
方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 **Non-Heap(非堆)**,目的应该是与 Java 堆区分开来。
180+
方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 **Java 虚拟机规范把方法区描述为堆的一个逻辑部分**,但是它却有一个别名叫做 **Non-Heap(非堆)**,目的应该是与 Java 堆区分开来。
148181
149182
方法区也被称为永久代。很多人都会分不清方法区和永久代的关系,为此我也查阅了文献。
150183
@@ -176,7 +209,7 @@ JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1
176209
177210
#### 2.5.3 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?
178211
179-
整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到 java.lang.OutOfMemoryError。你可以使用 `-XX:MaxMetaspaceSize` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。`-XX:MetaspaceSize` 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
212+
整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到 `java.lang.OutOfMemoryError`。你可以使用 `-XX:MaxMetaspaceSize` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。`-XX:MetaspaceSize` 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
180213
181214
当然这只是其中一个原因,还有很多底层的原因,这里就不提了。
182215

0 commit comments

Comments
 (0)