做B2C獨立網(wǎng)站的話需要做海外倉嗎人際網(wǎng)絡(luò)營銷2900
JVM內(nèi)存結(jié)構(gòu)
程序計數(shù)器
Program Counter Register程序計數(shù)器(寄存器)
程序計數(shù)器在物理層上是通過寄存器實現(xiàn)的
- 作用:記住下一條jvm指令的執(zhí)行地址
- 特點
- 是線程私有的(每個線程都有屬于自己的程序計數(shù)器)
- 不會存在內(nèi)存溢出
虛擬機棧(默認大小為1024kb)
- 每個線程運行時所需要的內(nèi)存稱為虛擬機棧
- 每個棧由多個棧幀組成,對應(yīng)著每次方法調(diào)用時所占的內(nèi)存
- 每個線程只能有一個活動棧幀,對應(yīng)著當(dāng)前正在執(zhí)行的那個方法
棧內(nèi)存溢出(StackOverflowError)
- 棧幀過多導(dǎo)致內(nèi)存溢出
- 棧幀過大導(dǎo)致內(nèi)存溢出
java編譯工具:
jstack 線程id:可以根據(jù)線程id找到有問題的線程進一步定位到有問題的代碼行數(shù)
本地方法棧(Native Method stack)
java代碼在完成一些需求時,需要調(diào)用一些底層的,如c/c++代碼,那么就需要本地方法棧
例如hashCode()方法,就是一個本地方法
public native int hashCode();/*** Indicates whether some other object is "equal to" this one.* <p>* The {@code equals} method implements an equivalence relation* on non-null object references:* <ul>* <li>It is <i>reflexive</i>: for any non-null reference value* {@code x}, {@code x.equals(x)} should return* {@code true}.* <li>It is <i>symmetric</i>: for any non-null reference values* {@code x} and {@code y}, {@code x.equals(y)}* should return {@code true} if and only if* {@code y.equals(x)} returns {@code true}.* <li>It is <i>transitive</i>: for any non-null reference values* {@code x}, {@code y}, and {@code z}, if* {@code x.equals(y)} returns {@code true} and* {@code y.equals(z)} returns {@code true}, then* {@code x.equals(z)} should return {@code true}.* <li>It is <i>consistent</i>: for any non-null reference values* {@code x} and {@code y}, multiple invocations of* {@code x.equals(y)} consistently return {@code true}* or consistently return {@code false}, provided no* information used in {@code equals} comparisons on the* objects is modified.* <li>For any non-null reference value {@code x},* {@code x.equals(null)} should return {@code false}.* </ul>** <p>* An equivalence relation partitions the elements it operates on* into <i>equivalence classes</i>; all the members of an* equivalence class are equal to each other. Members of an* equivalence class are substitutable for each other, at least* for some purposes.** @implSpec* The {@code equals} method for class {@code Object} implements* the most discriminating possible equivalence relation on objects;* that is, for any non-null reference values {@code x} and* {@code y}, this method returns {@code true} if and only* if {@code x} and {@code y} refer to the same object* ({@code x == y} has the value {@code true}).** In other words, under the reference equality equivalence* relation, each equivalence class only has a single element.** @apiNote* It is generally necessary to override the {@link #hashCode() hashCode}* method whenever this method is overridden, so as to maintain the* general contract for the {@code hashCode} method, which states* that equal objects must have equal hash codes.** @param obj the reference object with which to compare.* @return {@code true} if this object is the same as the obj* argument; {@code false} otherwise.* @see #hashCode()* @see java.util.HashMap*/
堆
堆(Heap)
- 通過new關(guān)鍵字,創(chuàng)建的對象都會使用堆內(nèi)存
- 特點:
- 他是線程共享的,堆中的對象需要考慮線程安全的問題
- 有垃圾回收機制
堆內(nèi)存溢出(OutOfMemoryError)
代碼演示
List<String> list = new ArrayList<>();
try{String a = "hello";while(true){list.add(a);a = a + a;}
}catch(Throwable e){e.printStackTrace()
}
堆內(nèi)存診斷
- jps工具:查看當(dāng)前系統(tǒng)中有哪些java進程
- jmap工具:查看堆內(nèi)存占用情況(jmap -heap 進程id)
- jconsole工具:圖形界面的,多功能的檢測工具,可以連續(xù)檢測
方法區(qū)
內(nèi)存溢出
- 1.8以前會導(dǎo)致永久代內(nèi)存溢出
- 1.8以后會導(dǎo)致原空間內(nèi)存溢出
運行時常量池
二進制字節(jié)碼包含:類基本信息,常量池,類方法定義包含了虛擬機指令
- 常量池就是一張表,虛擬機指令根據(jù)這張常量表找到要執(zhí)行的類名,方法名,參數(shù)類型,字面量等信息
- 運行時常量,常量池是*.class文件中的,當(dāng)該類被加載時,它的常量池信息就會放入運行時常量池,并把里面的符號地址變?yōu)檎鎸嵉刂?/li>
StringTable
底層是基于哈希表實現(xiàn),不能擴容
package bean;public class Temp {public static void main(String[] args) {String s1 = "a";String s2 = "b";String s3 = "ab";String s4 = s1 + s2;System.out.println(s3==s4);//false/*解釋:1. s3是在常量池中2. s4是new出來的字符串,在堆中3. 二者內(nèi)存空間地址不一樣,所以是false*/}
}
執(zhí)行javap -v Temp.class
Classfile /E:/code/java/temp/Thread/reflect/src/main/java/bean/Temp.classLast modified 2024年2月6日; size 780 bytesSHA-256 checksum 56c70e4c76c31646a9cb2cfece3728bc1a10f96185b4456e087039d1e60a0541Compiled from "Temp.java"
public class bean.Tempminor version: 0major version: 65flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #17 // bean/Tempsuper_class: #2 // java/lang/Objectinterfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool://這里是常量池#1 = Methodref #2.#3 // java/lang/Object."<init>":()V#2 = Class #4 // java/lang/Object#3 = NameAndType #5:#6 // "<init>":()V#4 = Utf8 java/lang/Object#5 = Utf8 <init>#6 = Utf8 ()V#7 = String #8 // a#8 = Utf8 a#9 = String #10 // b#10 = Utf8 b#11 = String #12 // ab#12 = Utf8 ab#13 = InvokeDynamic #0:#14 // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;#14 = NameAndType #15:#16 // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;#15 = Utf8 makeConcatWithConstants#16 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;#17 = Class #18 // bean/Temp#18 = Utf8 bean/Temp#19 = Utf8 Code#20 = Utf8 LineNumberTable#21 = Utf8 main#22 = Utf8 ([Ljava/lang/String;)V#23 = Utf8 SourceFile#24 = Utf8 Temp.java#25 = Utf8 BootstrapMethods#26 = String #27 // \u0001\u0001#27 = Utf8 \u0001\u0001#28 = MethodHandle 6:#29 // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;#29 = Methodref #30.#31 // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;#30 = Class #32 // java/lang/invoke/StringConcatFactory#31 = NameAndType #15:#33 // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;#32 = Utf8 java/lang/invoke/StringConcatFactory#33 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;#34 = Utf8 InnerClasses#35 = Class #36 // java/lang/invoke/MethodHandles$Lookup#36 = Utf8 java/lang/invoke/MethodHandles$Lookup#37 = Class #38 // java/lang/invoke/MethodHandles#38 = Utf8 java/lang/invoke/MethodHandles#39 = Utf8 Lookup
{public bean.Temp();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 3: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=5, args_size=10: ldc #7 // String a2: astore_13: ldc #9 // String b5: astore_26: ldc #11 // String ab8: astore_39: aload_110: aload_211: invokedynamic #13, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;16: astore 418: returnLineNumberTable:line 5: 0line 6: 3line 7: 6line 8: 9line 9: 18
}
SourceFile: "Temp.java"
BootstrapMethods:0: #28 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;Method arguments:#26 \u0001\u0001
InnerClasses:public static final #39= #35 of #37; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
- StringTable特性
- 常量池中的字符串僅是符號,第一次用到時才變?yōu)閷ο?/li>
- 利用串池的機制,來避免重復(fù)創(chuàng)建字符串對象
- 字符串拼接原理是StringBuilder(1.8)
- 字符串常量拼接的原理是編譯期優(yōu)化
- 可以使用intern方法,可以主動將串池中還沒有的字符串對象放入串池(1.8嘗試將字符串對象放入串池,如果有則并不會放入,如果沒有,則會把串池中的對象返回)
- StringTable位置
- 1.6之前,StringTable在永久代內(nèi)存空間中
- 1.8以后,StringTable在堆內(nèi)存空間中
- StringTable垃圾回收
- 超過閾值,會觸發(fā)GC垃圾回收
- StringTable性能調(diào)優(yōu)
- StringTable底層是基于哈希表實現(xiàn)的,所以StringTable調(diào)優(yōu),就是優(yōu)化底層哈希表的大小,以此減少哈希碰撞
- 參數(shù)
-XX:StringTableSize = 桶個數(shù)
- 考慮將字符串對象是否入池(包含很多重復(fù)的字符串)
直接內(nèi)存
定義:(操作系統(tǒng)內(nèi)存)
1. 常見與NIO操作,用于數(shù)據(jù)緩沖區(qū)
1. 分配回收成本較高,但讀寫性能高
1. 不受JVM內(nèi)存回收管理
內(nèi)存分配和釋放原理
是通過一個對象unsafe
來分配和釋放內(nèi)存的,并且回收需要主動調(diào)用freeMemory方法
ByteBuffer的實現(xiàn)類內(nèi)部,使用了Cleaner(虛引用)
來檢測ByteBuffer對象,一旦該對象被GC垃圾回收機制回收,那么就會由ReferenceHandler
線程通過Cleaner
的clean
方法調(diào)用freeMemory方法來釋放直接內(nèi)存
GC垃圾回收
如何判斷對象可以回收
-
引用計數(shù)法
如果有對象引用計數(shù)加一,沒有對象引用,計數(shù)減一,如果計數(shù)為零,則回收
但是如果存在循環(huán)引用,即A對象引用B對象,B對象引用A對象,會造成內(nèi)存泄漏
-
可達性分析算法
-
java虛擬機中的垃圾回收器采用可達性分析來探索所有存活的對象
-
掃描堆中的對象,看是否能夠沿著GC Root對象為起點的引用鏈找到該對象,找不到,表示可以回收
-
哪些對象可以作為GG Root?
-
System Class
例如:Object,String,HashMap等
-
Native Stack
-
Thread
-
Busy Monitor
-
-
-
四種引用
-
強引用:new的對象,賦值的對象
Object obj= new Object()//new 的對象都是是強引用
只要強引用存在,垃圾回收器將永遠不會回收被引用的對象,哪怕內(nèi)存不足時,JVM也會直接拋出OutOfMemoryError,不會去回收。如果想中斷強引用與對象之間的聯(lián)系,可以顯示的將強引用賦值為null,這樣一來,JVM就可以適時的回收對象了
-
軟引用
Object obj= new Object() SoftReference sr = new SoftReference<>(obj);//obj就是軟引用
在內(nèi)存足夠的時候,軟引用對象不會被回收,只有在內(nèi)存不足時,系統(tǒng)則會回收軟引用對象,如果回收了軟引用對象之后仍然沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。這種特性常常被用來實現(xiàn)緩存技術(shù),比如網(wǎng)頁緩存,圖片緩存等。
-
弱引用
Object obj= new Object() WeakReference wr = newWeakReference<>(obj);//obj就是軟引用
弱引用的引用強度比軟引用要更弱一些,無論內(nèi)存是否足夠,只要 JVM 開始進行垃圾回收,那些被弱引用關(guān)聯(lián)的對象都會被回收。在 JDK1.2 之后,用 java.lang.ref.WeakReference 來表示弱引用。
-
虛引用
Object obj= new Object() PhantomReference pr = new PhantomReference <>(obj);//obj就是軟引用
虛引用是最弱的一種引用關(guān)系,如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,它隨時可能會被回收,在 JDK1.2 之后,用 PhantomReference 類來表示,通過查看這個類的源碼,發(fā)現(xiàn)它只有一個構(gòu)造函數(shù)和一個 get() 方法,而且它的 get() 方法僅僅是返回一個null,也就是說將永遠無法通過虛引用來獲取對象,虛引用必須要和 ReferenceQueue 引用隊列一起使用。
-
終結(jié)器引用
無需手動編碼,但其內(nèi)部配合引用隊列使用,在垃圾回收時,終結(jié)器引用入隊(被引用對象暫時沒有被回收),再由Finalizer線程通過終結(jié)引用找到被引用對象并調(diào)用它的finalize方法,第二次GC時才能回收被引用的對象
-
垃圾回收算法
-
標(biāo)記清除
標(biāo)記:標(biāo)記的過程其實就是,遍歷所有的GC Roots,然后將所有GC Roots可達的對象標(biāo)記為存活的對象。
清除:清除的過程將遍歷堆中所有的對象,將沒有標(biāo)記的對象全部清除掉。
- 優(yōu)點:速度快
- 缺點:容易產(chǎn)生內(nèi)存碎片
-
標(biāo)記整理
標(biāo)記:標(biāo)記-整理算法從根對象開始遍歷整個對象圖,標(biāo)記所有與根對象可達的對象,即被引用的對象。
整理:標(biāo)記-整理算法會對內(nèi)存空間進行整理,將所有存活的對象移動到一端,而未標(biāo)記的對象則被認為是垃圾對象。
- 優(yōu)點:沒有產(chǎn)生"碎片"
- 缺點:速度慢
-
復(fù)制
將活著的內(nèi)存空間分為兩塊,每次只使用一塊,在垃圾回收時將正在使用的內(nèi)存塊中的存活的對象復(fù)制到未被使用的內(nèi)存塊,之后清除正在使用的內(nèi)存塊中的所有對象,交換兩塊內(nèi)存空間,完成垃圾回收
- 優(yōu)點:沒有標(biāo)記清除過程,運行高效,不會出現(xiàn)“碎片”問題
- 缺點:需要兩倍的內(nèi)存空間
分代垃圾回收
老年代:存放長時間使用的對象
新生代:存放用完就可以丟棄的對象
-
MinorGC
每次產(chǎn)生的新的對象在伊甸園區(qū),如果進行了垃圾回收,那么會進行遍歷所有對象,釋放所有的的未被使用的對象,將使用的對象通過復(fù)制的方式,復(fù)制到幸存區(qū)S1,并進行年齡+1操作,表示該對象每次回收都沒有被回收,當(dāng)該值超過一個閾值時,就會將該對象放入老年代區(qū),表示該對象可能會被長時間使用
- minor gc 會引發(fā)stop the world:進行垃圾回收時,會暫停其他的用戶線程,等垃圾回收后,用戶線程才恢復(fù)運行
- 當(dāng)對象壽命超過閾值時,會晉升到老年區(qū),最大壽命是15(4bit)
-
FullGC
老年代空間都不足時,先觸發(fā)Minorgc,如果之后內(nèi)存空間仍然不足,則會觸發(fā)FullGC,來對內(nèi)存空間進行清理,STW(stop the world)的時間更長
相關(guān)VM的參數(shù)
大對象:如果說某個對象的大小超過了新生代內(nèi)存大小,那么會將這個對象直接晉升到老年代內(nèi)存塊,不會發(fā)生GC回收
垃圾回收器
-
串行
-
單線程的垃圾回收器
-
適用于堆內(nèi)存較小的場景,個人電腦
-
打開串行垃圾回收器 -XX:+UseSerialGC=Serial(新生代:復(fù)制算法) + SerialOld(老年代:標(biāo)記整理算法)
-
-
-
吞吐量優(yōu)先(1.8默認的垃圾回收器)
-
多線程的垃圾回收器
-
適用于堆內(nèi)存較大的場景,需要多核CPU的支持
-
讓單位時間內(nèi)STW的時間最短
-
在進行垃圾回收時會占滿CPU
-
-
-XX:parallelGCThreads=n //指定線程數(shù)量 -XX:UseAdaptiveSizePolicy //采用自適應(yīng)的大小調(diào)整策略,調(diào)整新生代的大小 -XX:UseParallelGC //開啟吞吐量優(yōu)先垃圾回收器 //ratio默認為99,此時值為0.01,只能由1%的時間用來進行垃圾回收(100min,只能有1min進行垃圾回收) -XX:GCTimeRatio=ratio //(公式:1/1+ratio)
-
-
響應(yīng)時間優(yōu)先(基于標(biāo)記清除算法)
-
多線程的垃圾回收器
-
適用于堆內(nèi)存較大的場景,需要多核CPU的支持
-
盡可能的讓單次STW的時間最短
-
-
-XX:UseConcMarkSweepGC
-
G1(JDK9之后的默認的垃圾回收器)
定義:Garbage First
- 適用場景
- 同時注重吞吐量和低延時,默認的暫時目標(biāo)是200ms
- 超大堆內(nèi)存,會將堆劃分為多個大小相等的Region
- 整體上是標(biāo)記+整理算法,兩個區(qū)域之間是復(fù)制算法
- 相關(guān)參數(shù)
-XX:+UseG1GC
-XX:G1HeapRegionSize=size //設(shè)置Region的大小(1,2,4,8,16)
-XX:MaxGCPauseMillis=time //暫停目標(biāo)時間
-
G1垃圾回收階段
3.1Young Collection(新生代)
? 會STW
? 會將幸存的對象,復(fù)制到幸存區(qū),之后新生代內(nèi)存滿了,會將部分常用的對象復(fù)制到老年代內(nèi)存
3.2Young Collection+Concurrent Mark(新生代+并發(fā)標(biāo)記)
? 在YoungGC時會進行GC Root的初始標(biāo)記
? 老年代占用堆空間比例達到閾值時(默認為45%),進行并發(fā)標(biāo)記(不會STW)
3.3Mixed Collection(混合收集)
? 會對伊甸園,新生區(qū),老年區(qū)進行全面垃圾回收
? 最終標(biāo)記會STW
? 拷貝存活會STW
-
Young Collection 跨代引用
新生代回收的跨代引用(老年代引用新生代)問題
垃圾回收調(diào)優(yōu)
- 調(diào)優(yōu)領(lǐng)域
- 內(nèi)存
- 鎖競爭
- cpu占用
- io
- 最快的GC是不發(fā)生GC
- 查看FullGC前后內(nèi)存占用,考慮幾個問題
- 是不是數(shù)據(jù)太多了
- 數(shù)據(jù)表示是否太臃腫
- 是否存在內(nèi)存泄漏
- 查看FullGC前后內(nèi)存占用,考慮幾個問題
- 新生代調(diào)優(yōu)
- 所有new操作的內(nèi)存分配非常廉價
- 死亡對象的回收代價為零
- 大部分對象用過即死
- minorgc時間遠遠低于fullgc
- 所以新生代內(nèi)存塊的大小應(yīng)該設(shè)置為堆大小的1/4~1/2最佳,過大的話,會導(dǎo)致老年代內(nèi)存過小,發(fā)生頻繁 FullGC
- 幸存區(qū)要足夠大到能保留當(dāng)前活躍對象和需要晉升的對象
- 晉升閾值配置要得當(dāng),讓長時間存活對象能盡快晉升
- 老年代調(diào)優(yōu)(CMS)
- CMS的老年代內(nèi)存越大越好
- 將老年代內(nèi)存預(yù)設(shè)調(diào)至1/4~1/3
Class字節(jié)碼與類加載器
類文件結(jié)構(gòu)
0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09
0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07
-
majic
0~3字節(jié),表示他是否是class類型的文件
ca fe ba be
-
version
4~7字節(jié),表示類的版本00 34(52) 表示是java8(53表示jdk9)
-
常量池
8~9字節(jié),表示常量池的長度,00 23(35)表示常量池有#1~#34項,注意#0項不計入,也沒有值
第#1項0a表示一個Method信息,00 06 和 00 15(21)表示它引用了常量池中的#6和#21項來獲得這個方法的所屬類和方法名
第#2項09表示一個Filed信息,00 16(22)和00 17(23)表示它引用了常量池中的#22和#23項來獲得這個方法的所屬類和方法名
JIT compiler 即時編譯器
1/3
Class字節(jié)碼與類加載器
類文件結(jié)構(gòu)
[外鏈圖片轉(zhuǎn)存中…(img-0dFWAySR-1707834738029)]
0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09
0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07
-
majic
0~3字節(jié),表示他是否是class類型的文件
ca fe ba be
-
version
4~7字節(jié),表示類的版本00 34(52) 表示是java8(53表示jdk9)
-
常量池
8~9字節(jié),表示常量池的長度,00 23(35)表示常量池有#1~#34項,注意#0項不計入,也沒有值
第#1項0a表示一個Method信息,00 06 和 00 15(21)表示它引用了常量池中的#6和#21項來獲得這個方法的所屬類和方法名
第#2項09表示一個Filed信息,00 16(22)和00 17(23)表示它引用了常量池中的#22和#23項來獲得這個方法的所屬類和方法名