@@ -136,15 +136,48 @@ Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共
136
136
137
137
Java 堆是垃圾收集器管理的主要区域,因此也被称作** GC 堆(Garbage Collected Heap)** .从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。** 进一步划分的目的是更好地回收内存,或者更快地分配内存。**
138
138
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
+ >
144
177
145
178
### 2.5 方法区
146
179
147
- 方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 ** Non-Heap(非堆)** ,目的应该是与 Java 堆区分开来。
180
+ 方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 ** Java 虚拟机规范把方法区描述为堆的一个逻辑部分** ,但是它却有一个别名叫做 **Non-Heap(非堆)**,目的应该是与 Java 堆区分开来。
148
181
149
182
方法区也被称为永久代。很多人都会分不清方法区和永久代的关系,为此我也查阅了文献。
150
183
@@ -176,7 +209,7 @@ JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1
176
209
177
210
#### 2.5.3 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?
178
211
179
- 整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到 java.lang.OutOfMemoryError。你可以使用 ` -XX:MaxMetaspaceSize ` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。` -XX:MetaspaceSize ` 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
212
+ 整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到 ` java.lang.OutOfMemoryError ` 。你可以使用 ` -XX:MaxMetaspaceSize ` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。` -XX:MetaspaceSize ` 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
180
213
181
214
当然这只是其中一个原因,还有很多底层的原因,这里就不提了。
182
215
0 commit comments