理解JVM(2) 棧記憶體,方法區,堆記憶體
阿新 • • 發佈:2019-01-10
堆,方法區,棧的關係
分配最大堆記憶體-Xmx32m
class SimpleHeap(val id: Int){
fun show() = println("My id is $id")
}
fun main(args: Array<String>) {
val s1 = SimpleHeap(1)
val s2 = SimpleHeap(2)
s1.show()
s2.show()
}
方法區內儲存類的基本資訊,包括方法的實現。方法區裡面的資訊很少清除
Java堆內儲存著s1,s2的例項
Java棧內儲存著s1和s2的方法show()的區域性變數
棧的溢位測試
棧幀包括:區域性變量表(原生型別或引用型別的物件引用),運算元棧(類似於暫存器結構,用於計算),幀資料區(常量池指標和異常處理表)
分配最大棧記憶體-Xss228K
var count = 0
class SimpleHeap{
fun show(){
count++
val KB = ByteArray(1024*10)
KB.set(count, count.toByte());
show()
}
}
上面程式碼,我以為是儲存了10K的區域性變數,後來發現數組還是放在堆記憶體裡面的,棧中只儲存一個引用。所以還是能遞迴3000次吧
var count = 0
class SimpleHeap{
fun show(){
count++
val a = 1L
val b = 2L
val c = 3L
val d = 4L
val e = 5L
val f = 6L
val g = 7L
val h = 8L
val i = 9L
val j = 10L
show()
}
}
一共呼叫670次,每次呼叫會使用350個位元組,然後區域性變數會儲存80位元組的long型區域性變數
堆記憶體回收分析
class SimpleHeap{
fun gc1(){
val MB = ByteArray(1024*1024*6)
System.gc() //不會馬上回收記憶體
}
fun gc2(){
var MB: ByteArray? = ByteArray(1024*1024*6)
MB = null
System.gc()
}
fun gc3(){
{
var MB = ByteArray(1024*1024*6)
}
System.gc()
}
fun gc4(){
{
var MB = ByteArray(1024*1024*6)
}
val c = 10
System.gc()
}
fun gc5(){
gc1()
System.gc()
}
}
gc1()
可以看到沒有回收記憶體。
gc2()
可以發現,又多分配了6MB,然後馬上回收,這次一次性回收了12MB,因為gc1()的6MB也給回收了。
gc3(),gc4(),gc5()
gc3()gc4()不能為什麼,根本沒有分配記憶體,說不定給Kotlin編譯器給優化了。
gc5()在gc1()退出作用域後,直接回收掉了6MB
棧上分配記憶體
對於那些執行緒私有的物件(指不會被其他執行緒訪問到的物件),可以打散分配在棧上,而不是分配在堆上。在函式呼叫後自行下載,而不用垃圾收集器。
實現的技術是進行逃逸分析-XX:+DoEscapeAnalysis
/*
-server -Xmx10m -Xms10m
-XX:+PrintGC -XX:+DoEscapeAnalysis
-XX:-UseTLAB -XX:+EliminateAllocations
*/
class OnStackTest{
class User(val id:Int = 0, val name:String = ""){
}
companion object {
fun alloc(){
val u = User(5,"owen")
}
}
}
fun main(args: Array<String>) {
val b = System.currentTimeMillis()
for (i in 0..1000000000){
OnStackTest.alloc()
}
val c = System.currentTimeMillis()
println(c - b)
}
要呼叫100000000次,按理說會頻繁呼叫GC,但棧上分配技術顯示, 咳咳,按理說是看不到GC日誌
[GC (Allocation Failure) 2047K->544K(9728K), 0.0032233 secs]
[GC (Allocation Failure) 2592K->472K(11776K), 0.0080457 secs]
60