您好,登錄后才能下訂單哦!
這篇文章主要介紹JVM系列之內(nèi)存模型的示例分析,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!
這一章學(xué)習(xí)java虛擬機(jī)內(nèi)存模型(Java Virtual machine menory model),可以這樣理解,jvm運(yùn)行時數(shù)據(jù)庫是一種規(guī)范,而JVM內(nèi)存模型是對改規(guī)范的實(shí)現(xiàn)
java虛擬機(jī)重點(diǎn)存儲數(shù)據(jù)的是堆和方法區(qū),所以本章節(jié)也重點(diǎn)從這兩個方面進(jìn)行比較詳細(xì)描述。堆和方法區(qū)是內(nèi)存共享的,而java虛擬機(jī)棧、Native方法棧、程序計(jì)數(shù)器是線程私有的
一個是非堆區(qū)(方法區(qū)),方法區(qū)也一般被稱之為“永久代”。另外一個是堆區(qū),分為young區(qū)和old區(qū),young區(qū)又分為兩個部分,一個是Eden區(qū),一個是Survivor區(qū)(S0+S1),S0區(qū)也可以稱之From區(qū),S1也可以稱之為To區(qū)
為什么需要Survivor區(qū)?只有Eden不行嗎?
假設(shè)不設(shè)計(jì)出Survivor區(qū),Eden區(qū)進(jìn)行一次MinorGC,對象就直接被送到Old區(qū),這樣一來Old區(qū)很快就被填滿,Old區(qū)一滿,就會進(jìn)行FullGC(Old區(qū)會進(jìn)行MajorGC,一般伴隨著MinorGC),F(xiàn)ullGC是很耗時的,所以設(shè)計(jì)出Survivor區(qū)的目的是減少對象被送到Old區(qū),有一個過渡的Survivor區(qū)
補(bǔ)充:Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代
Eden:S1:S2是8:1:1
需要兩個Survivor區(qū)的目的是為了避免內(nèi)存碎片化。為什么這么說?
假設(shè)只設(shè)計(jì)出一個Survivor區(qū),一旦Eden區(qū)滿了,就會進(jìn)行Minor GC,Eden區(qū)存活的對象就會被移動到Survivor區(qū),等下一次Eden區(qū)滿時候,問題就來了,進(jìn)行MinorGC就將Eden區(qū)對象硬放到Survivor區(qū),這樣就導(dǎo)致了對象所占的內(nèi)存是不連續(xù)的
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
使用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ū)測試接口:
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
在前面學(xué)習(xí),java虛擬機(jī)棧是通過棧幀方式存儲,一個方法對應(yīng)一個棧幀,按照隊(duì)列模式進(jìn)棧,所以要測試程序?qū)е耲ava虛擬機(jī)棧出現(xiàn)問題,可以通過遞歸方法方式進(jìn)行測試:
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è)置每個線程的堆棧大小。JDK 5以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K
以上是“JVM系列之內(nèi)存模型的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。