一.談談你對java的理解
1.java與平臺無關:一次編譯,處處運行
2.GC,不需要我們手動的回收垃圾
3.語言特性:泛形,反射,Lamda表達式
4.面向?qū)ο螅悍庋b,繼承,多態(tài)
5.類庫:JUC,IO,集合
6.異常處理:
二.java是如何實現(xiàn)與平臺無關的
編譯:javac,將源碼編譯成字節(jié)碼class文件
class文件保存的是java文件翻譯成的二進制字節(jié)碼,還會添加一個共有的靜態(tài)常量屬性.class,這個屬性記錄類的相關信息及類型信息,是class的一個實例,
Javap:是JDK自帶的反編譯命令,在命令行當中可以執(zhí)行。

運行:JVM虛擬機會把.class文件解析成本平臺可以識別的二進制機器碼,。class文件僅僅是面向JVM的。
三.JVM如何加載.class文件
JVM是一個內(nèi)存當中的虛擬機

JVM主要由Class Loader,Runtime Data Area,Rxcution Engine,Native Interface幾部分組成,它主要通過Class Loader將符合其格式的.class文件加載到內(nèi)存里面,并通過Execution Engine對class里面的字節(jié)碼進行解析,并提交給操作系統(tǒng)去執(zhí)行
Native Interface(本地接口):主要作用是融合不同開發(fā)語言的庫(C/C++),供給java調(diào)用。
1.Class Loader
Class Loader在java當中有非常重要的作用,它主要工作在class加載的記載階段,其作用是將從系統(tǒng)外部獲得字節(jié)碼文件加載到系統(tǒng)當中,然后由jvm進行連接/初始化等工作,他是java的核心組件,所有的class都是通過它加載的。
Class Loader類型:
BootStrapClassLoader(啟動類加載器):C++編寫,加載核心庫,java.
ExtClassLoader(標準類加載器):java編寫,加載擴展庫,javax.
AppClassLoader:java編寫,加載程序所在目錄
自定義ClassLader:java編寫,定制化加載
2.class類的加載過程
加載--->連接--->初始化--->使用--->卸載

類的加載方式:
1.隱式:new
2.顯示:ClassLoader,Class.forName()
-- 對于顯示加載來說,當我們獲得到Class對象(clazz)以后,可以調(diào)用Class對象的newInstance()方法來生成對象的實例(root),
加載步奏:
1. 通過一個類的全限名來獲取定義此類的二進制節(jié)流。(實現(xiàn)這個代碼模塊就是類加載器)
2. 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)。
3. 在內(nèi)存中生成一個代表這個類的 java.lang.Class 對象,作為方法區(qū)這個類的各種數(shù)據(jù)的 訪問入口。
3.類加載的雙親委派機制

假設有個類Person.class,當類加載器收到加載類的請求時,它首先會去檢查自定義ClassLoader是否加載過這個類,如果沒有,就把這個請求轉(zhuǎn)交給父類加載器
去完成,依次類推,當Bootstrap ClassLoader也沒有加載過這個類時,就會去自己的路徑下加載,有就返回,沒有就轉(zhuǎn)交給自己的下一級Extension Class Loader去加載。
4.破壞雙親委派模型
1.在 JDK1.2 之前,用戶去繼承 java.lang.ClassLoader 的唯一目的就是為了重寫 loadClass
方法,由于用戶自己重寫了 loadClass,那么也就是用戶自己去自定義加載類,會破壞。

自定義ClassLoader:
1.繼承java.lang.ClassLoader
2.重寫fingClass(),defindClass()
四.JVM內(nèi)存區(qū)域劃分
參考自:[https://blog.csdn.net/javazejian/article/details/72772461]

Java虛擬機在運行程序時會把其自動管理的內(nèi)存劃分為以上幾個區(qū)域,每個區(qū)域都有的用途以及創(chuàng)建銷毀的時機,其中藍色部分代表的是所有線程共享的數(shù)據(jù)區(qū)域,而綠色部分代表的是每個線程的私有數(shù)據(jù)區(qū)域。
1.程序計數(shù)器:
是一塊較小的內(nèi)存空間,在JVM當中,字節(jié)碼解釋器工作時就是通過改變計數(shù)器的值來選取下一跳需要執(zhí)行的字節(jié)碼指令,包括分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復等基礎功能,,都需要借助這個計數(shù)器來完成。由于JVM的多線程是通過線程切換并分派處理器來完成的,在一個確定的時刻,一個處理器只會處理一個線程當中的指令,因此為了線程切換后可以恢復到正確的位置,程序計數(shù)器設為私有的,記錄線程執(zhí)行步奏
程序計數(shù)器,只會為java方法計數(shù),其他的native方法不會計數(shù)
由于程序記錄器只是記錄了行號,所以程序計數(shù)器不會有內(nèi)存泄漏的問題
2.Java虛擬機棧
包括多個棧幀,即方法運行期間的基礎數(shù)據(jù)結(jié)構(gòu),當方法調(diào)用結(jié)束時,幀才會被銷毀,虛擬機棧又稱為java堆棧。屬于線程私有的數(shù)據(jù)區(qū)域,與線程同時創(chuàng)建,每個方法執(zhí)行時都會創(chuàng)建一個棧楨來存儲方法的的變量表、操作數(shù)棧、動態(tài)鏈接方法、返回值、返回地址等信息。
每次方法調(diào)用時,一個新的棧幀創(chuàng)建,并壓棧到棧頂,當方法正常返回或者拋出未俘獲的異常是,棧幀就會出棧,除了棧幀的壓棧和出棧,棧不能直接被操作。

虛擬機棧也是JVM管理的,它類似與一個集合,但是有固定長度,是由多個棧幀合起來的,編寫程序的時候,每調(diào)用一個方法,JVM就會自動在內(nèi)存當中分配一塊內(nèi)存空間(棧幀),方法調(diào)用結(jié)束以后,棧幀就會被自動釋放掉,這也就是我們常說的,棧的內(nèi)存不會通過GC去回收,而是會自己釋放。
3.本地方法棧
和虛擬機棧相比:本地方法棧主要作用與標注了native的方法。
java方法用到的是虛擬機棧,而帶有native關鍵字的方法用到的是本地方法棧。
4.方法區(qū)
方法區(qū)只是一個邏輯概念,《Java虛擬機規(guī)范》只是規(guī)定了有方法區(qū)這么個概念和它的作用,并沒有規(guī)定如何去實現(xiàn)它。方法區(qū)的實現(xiàn)一般有兩種,即MetaSpace(元空間)和PermGen(永久代)。
主要用于存儲已被虛擬機加載的類信息(包括class對象的method,field等)、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù),根據(jù)Java 虛擬機規(guī)范的規(guī)定,當方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryError 異常。
在JDK1.8以后,開始把類的元數(shù)據(jù)存放在本地堆內(nèi)存當中,這一塊區(qū)域就叫做MetaSpace,該區(qū)域在8之前是屬于永久帶的,原數(shù)據(jù)空間和永久代都是用來存放Class的相關信息的,包括class對象的method,field等,元空間和永久代皆是方法去的實現(xiàn),只是實現(xiàn)的方法不一樣,所以說方法區(qū)只是一種JVM的規(guī)范。

理論上本地內(nèi)存有多大,MetaSpace就有多大,這解決了空間不足的問題,不過也不可能無限大,JVM會根據(jù)需要動態(tài)的確定其大小.

永久代會為GC帶來不必要的復雜度,而且效率偏低,可能會隨著每一次GC而發(fā)生移動,PermGen的每一種垃圾回收器都需要特殊處理永久代當中的信息.
5.堆
Java 堆也是屬于線程共享的內(nèi)存區(qū)域,它在虛擬機啟動時創(chuàng)建,是Java 虛擬機所管理的內(nèi)存中最大的一塊,主要用于存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存,注意Java 堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱做GC 堆,如果在堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴展時,將會拋出OutOfMemoryError 異常。
JDK.7以后,原來位于方法去里面的常量池被移到了java堆當中,并且8以后使用元空間替代了永久代.

現(xiàn)在的垃圾回收器都是按照分代收集算法,java堆可以分為新生代和老年代,在細分為Eden,Survivor。
6.發(fā)生在虛擬機棧當中的StackOverflowError(棧溢出)和OutOfMemorryError(內(nèi)存泄漏)
1.StackOverflowError(棧溢出)

StackOverflowError(棧溢出):遞歸的程序太多了
當線程執(zhí)行一個方法時,就會創(chuàng)建一個對應的棧幀,并將建立的棧幀壓入虛擬機棧當中,方法執(zhí)行完畢的時候,就會將棧幀出棧,因此線程當前所執(zhí)行的方法所對應的棧幀必定在棧的頂部,而我們的遞歸函數(shù)不斷去調(diào)用自己,每一次方法調(diào)用就會生成一個棧幀,而且會保存當中當前方法的棧幀狀態(tài),將它放在JVM棧當中,棧幀上下文切換時候,可以切換到最新的方法棧幀當中。而由于我們每個線程的虛擬機棧的深度是固定的,遞歸實現(xiàn)將導致棧深度的增加,每次遞歸都會往棧里面壓一個棧幀,如果超出了最大允許的深度,就會報錯。
2.OutOfMemorryError
OutOfMemorryError(內(nèi)存泄漏):
當虛擬機??梢詣討B(tài)擴展時,無法申請足夠多的內(nèi)存就會報這個錯誤,



五.JVM三大性能調(diào)優(yōu)參數(shù)—Xms —Xmx —Xss的含義
-Xss:規(guī)定了每個線程虛擬機棧的大小,一般256即可,此配置將會影響此進程當中并發(fā)線程數(shù)的大小
-Xms:初始的Java堆的大小,和該進程剛創(chuàng)建出來的時候,專屬Java堆的大小,當對象容量超過了java堆的大小,就會擴容到 -Xmx大小
-Xmx:java堆能夠達到的最大值

在很多的場合當中,我們通常將-Xms和-Xmx設置成一樣的,因為當堆不夠用而擴容時,發(fā)生內(nèi)存抖動,影響程序運行時的穩(wěn)定性。
六.java內(nèi)存當中棧堆的區(qū)別和內(nèi)存分配策略
程序運行時有三種內(nèi)存分配策略,靜態(tài)的,棧式的,堆式的

靜態(tài)的:在編譯時就可以確定運行時在內(nèi)存當中的存儲需求。
棧式的:可以稱為動態(tài)的存儲分配,是由一個類似于堆棧的運行棧來實現(xiàn)的,
堆式的:可變長度串,對象實例
JVM自己可以根據(jù)內(nèi)存棧進行管理操作,
堆空間即即使是有垃圾回收機制,還是要考慮其釋放的問題,棧的效率高于堆

七.內(nèi)存泄露和內(nèi)存溢出
