詳解JVM系列之內(nèi)存模型
1. 內(nèi)存模型和運(yùn)行時(shí)數(shù)據(jù)區(qū)
這一章學(xué)習(xí)java虛擬機(jī)內(nèi)存模型(Java Virtual machine menory model),可以這樣理解,jvm運(yùn)行時(shí)數(shù)據(jù)庫(kù)是一種規(guī)范,而JVM內(nèi)存模型是對(duì)改規(guī)范的實(shí)現(xiàn)

java虛擬機(jī)重點(diǎn)存儲(chǔ)數(shù)據(jù)的是堆和方法區(qū),所以本章節(jié)也重點(diǎn)從這兩個(gè)方面進(jìn)行比較詳細(xì)描述。堆和方法區(qū)是內(nèi)存共享的,而java虛擬機(jī)棧、Native方法棧、程序計(jì)數(shù)器是線程私有的

2、思維導(dǎo)圖和圖例

一個(gè)是非堆區(qū)(方法區(qū)),方法區(qū)也一般被稱之為“永久代”。另外一個(gè)是堆區(qū),分為young區(qū)和old區(qū),young區(qū)又分為兩個(gè)部分,一個(gè)是Eden區(qū),一個(gè)是Survivor區(qū)(S0+S1),S0區(qū)也可以稱之From區(qū),S1也可以稱之為To區(qū)

3、對(duì)象向JVM申請(qǐng)空間

4、為什么需要Survivor區(qū)?
為什么需要Survivor區(qū)?只有Eden不行嗎?
假設(shè)不設(shè)計(jì)出Survivor區(qū),Eden區(qū)進(jìn)行一次MinorGC,對(duì)象就直接被送到Old區(qū),這樣一來Old區(qū)很快就被填滿,Old區(qū)一滿,就會(huì)進(jìn)行FullGC(Old區(qū)會(huì)進(jìn)行MajorGC,一般伴隨著MinorGC),F(xiàn)ullGC是很耗時(shí)的,所以設(shè)計(jì)出Survivor區(qū)的目的是減少對(duì)象被送到Old區(qū),有一個(gè)過渡的Survivor區(qū)
補(bǔ)充:Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代
Eden:S1:S2是8:1:1
5、為什么需要兩個(gè)Survivor區(qū)?
需要兩個(gè)Survivor區(qū)的目的是為了避免內(nèi)存碎片化。為什么這么說?
假設(shè)只設(shè)計(jì)出一個(gè)Survivor區(qū),一旦Eden區(qū)滿了,就會(huì)進(jìn)行Minor GC,Eden區(qū)存活的對(duì)象就會(huì)被移動(dòng)到Survivor區(qū),等下一次Eden區(qū)滿時(shí)候,問題就來了,進(jìn)行MinorGC就將Eden區(qū)對(duì)象硬放到Survivor區(qū),這樣就導(dǎo)致了對(duì)象所占的內(nèi)存是不連續(xù)的
6、例子進(jìn)行驗(yàn)證
堆內(nèi)存溢出
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class HeapController {
List<Foo> list = new ArrayList<Foo>();
@GetMapping(value = {"heap"})
public String heapTest() {
while (true) {
list.add(new Foo());
}
}
@Data
class Foo {
String str;
}
}
訪問接口,出現(xiàn)內(nèi)存溢出;
java.lang.OutOfMemoryError: Java heap space
...
可以設(shè)置參數(shù):比如-Xms64M -Xmx512M
方法區(qū)內(nèi)存溢出
使用asm,maven配置:
<dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.3.1</version> </dependency>
編寫代碼,向方法區(qū)中添加Class的信息,注意,電腦性能不夠好,不要執(zhí)行此代碼,很容易,造成電腦重啟,太吃內(nèi)存,也可以調(diào)小循環(huán)次數(shù)
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.List;
public class MyMetaspace extends ClassLoader {
public static List<Class<?>> createClasses() {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
"java/lang/Object", null);
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
"()V", null, null);
mw.visitVarInsn(Opcodes.ALOAD, 0);
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
"<init>", "()V");
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
MyMetaspace test = new MyMetaspace();
byte[] code = cw.toByteArray();
Class<?> exampleClass = test.defineClass("Class" + i, code, 0,
code.length);
classes.add(exampleClass);
}
return classes;
}
}
方法區(qū)測(cè)試接口:
import com.example.jvm.jvmexceptionexample.asm.MyMetaspace;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class NonHeapController {
List<Class<?>> list = new ArrayList<Class<?>>();
@GetMapping(value = {"/noheap"})
public String noheap() {
while (true) {
list.addAll(MyMetaspace.createClasses());
}
}
}
java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.5_54]
處理方法,設(shè)置Metaspace的大小,比如-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=512M
Java虛擬機(jī)棧
在前面學(xué)習(xí),java虛擬機(jī)棧是通過棧幀方式存儲(chǔ),一個(gè)方法對(duì)應(yīng)一個(gè)棧幀,按照隊(duì)列模式進(jìn)棧,所以要測(cè)試程序?qū)е耲ava虛擬機(jī)棧出現(xiàn)問題,可以通過遞歸方法方式進(jìn)行測(cè)試:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StackController {
public static long count = 0;
public static void add(long i) {
count ++ ;
add(i);
}
@GetMapping(value = {"stack"})
public void stack() {
add(1);
}
}
StackOverflow,棧溢出異常:
java.lang.StackOverflowError: null
at com.example.jvm.jvmexceptionexample.controller.StackController.add(StackController.java:14) ~[classes/:na]
處理方法,設(shè)置-Xss256k:設(shè)置每個(gè)線程的堆棧大小。JDK 5以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K
以上就是詳解JVM系列之內(nèi)存模型的詳細(xì)內(nèi)容,更多關(guān)于JVM 內(nèi)存模型 內(nèi)存結(jié)構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript 表格排序和表頭浮動(dòng)效果(擴(kuò)展SortTable)
前段時(shí)間一個(gè)項(xiàng)目有大量頁(yè)面用到表格排序和表頭浮動(dòng)的效果,在網(wǎng)上找了幾個(gè)表格排序的js代碼,最后選擇了 Stuart Langridge的SortTable,在SortTable基礎(chǔ)上做了些擴(kuò)展,加上了表頭浮動(dòng)效果及一些小功能。2009-04-04
JavaScript如何將后端獲取到的byte數(shù)組轉(zhuǎn)為文件
這篇文章主要給大家介紹了關(guān)于JavaScript如何將后端獲取到的byte數(shù)組轉(zhuǎn)為文件的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-04-04
微信小程序MUI側(cè)滑導(dǎo)航菜單示例(Popup彈出式,左側(cè)不動(dòng),右側(cè)滑動(dòng))
這篇文章主要介紹了微信小程序MUI側(cè)滑導(dǎo)航菜單,結(jié)合實(shí)例形式分析了微信小程序Popup彈出式,左側(cè)不動(dòng),右側(cè)滑動(dòng)菜單相關(guān)實(shí)現(xiàn)技巧與注意事項(xiàng),需要的朋友可以參考下2019-01-01
javascript檢測(cè)頁(yè)面是否縮放的小例子
簡(jiǎn)單寫了下,只測(cè)試了chrome,道理很簡(jiǎn)單,其他瀏覽器自行擴(kuò)充。2013-05-05
詳解JavaScript如何準(zhǔn)確獲取任意變量的數(shù)據(jù)類型
js是弱類型語(yǔ)言,或者說是動(dòng)態(tài)語(yǔ)言,在定義變量時(shí)我們可以不提前聲明變量的類型,也可以在變量聲明后賦予不同類型的值。所以本文為大家詳解一下JavaScript如何準(zhǔn)確獲取任意變量的數(shù)據(jù)類型,需要的可以參考一下2022-06-06
跟我學(xué)習(xí)javascript的浮點(diǎn)數(shù)精度
跟我學(xué)習(xí)javascript的浮點(diǎn)數(shù)精度,帶大家真正的理解JavaScript的浮點(diǎn)數(shù),提醒大家當(dāng)心浮點(diǎn)運(yùn)算中的精度陷阱,需要的朋友可以參考下2015-11-11
深入淺出JSON.parse的實(shí)現(xiàn)方法
眾所周知,JSON.parse方法用于將一個(gè)json字符串轉(zhuǎn)換成由字符串描述的?JavaScript?值或?qū)ο?本文主要為大家介紹了JSON.parse方法的使用,需要的可以參考下2024-04-04
JS實(shí)現(xiàn)根據(jù)用戶輸入分鐘進(jìn)行倒計(jì)時(shí)功能
倒計(jì)時(shí)功能大家無(wú)論在各大網(wǎng)站都可以看到,今天小編給大家分享一段基于js實(shí)現(xiàn)的根據(jù)用戶輸入分鐘進(jìn)行倒計(jì)時(shí)功能,非常不錯(cuò),需要的朋友參考下吧2016-11-11

