Python股票資料分析
JVM探究
- 請你談談你對JVM的理解?
- java8虛擬機器和之前的變化更新?
- 什麼是OOM,什麼是棧溢位StackOverFlowError?怎麼分析?
- JVM的常用調優引數有哪些?
- 記憶體快照如何抓取,怎麼分析Dump檔案?知道嗎?
- 談談JVM中,類載入器你的認識?
1、JVM的位置
2、JVM的體系結構
3、類載入器
作用:載入Class檔案~
類是模板,物件是具體的
- 虛擬機器自帶的載入器
- 啟動類(根)載入器
- 擴充套件類載入器
- 應用程式載入器
package com.jvm; public class Car { public int age; public static void main(String[] args) { //類是模板,物件是具體的 Car car1 = new Car(); Car car2 = new Car(); Car car3 = new Car(); System.out.println(car1.hashCode()); System.out.println(car2.hashCode()); System.out.println(car3.hashCode()); Class<? extends Car> aClass1 = car1.getClass(); ClassLoader classLoader = aClass1.getClassLoader(); //AppClassLoader 應用程式載入器 System.out.println(classLoader); System.out.println(classLoader.getParent()); //ExtClassLoader 擴充套件類載入器 \jre\lib\ext System.out.println(classLoader.getParent().getParent()); // null 不存在 或 java程式獲取不到 } }
4、雙親委派機制
package java.lang; public class String { //雙親委派機制:安全 /** * 1.App-->EXC-->BOOT(最終執行) * 2.Boot * 3.EXC * 4.APP * */ @Override public String toString(){ return "hello"; } public static void main(String[] args) { String s = new String(); System.out.println(s.getClass().getClassLoader()); s.toString(); new Thread().start(); } /*** *1.類載入器收到類載入的請求 application * 2.將這個請求向上委託給父類載入器去完成,一直向上委託,知道啟動類載入器 * 3.啟動載入器檢查是否能夠載入當前這個類,能載入就結束,使用當前的載入器,否則,丟擲異常,通知自家在其進行載入 * 重複步驟3 * Class not Found ~ * * null :java呼叫不到~ C、C++ * java = C++:去掉繁瑣的東西,指標,記憶體管理 C++-- */ }
package com.jvm; public class Student { @Override public String toString() { return "hello"; } public static void main(String[] args) { Student student = new Student(); System.out.println(student.getClass().getClassLoader()); //AppClassLoader System.out.println(student.toString()); } }
5、沙箱安全機制
Java安全模型的核心就是ava沙箱(sandbox),什麼是沙箱?沙箱是一個限制程式執行的環境。沙箱機制就是將」va程式碼限定在虛擬機器(UVM)特定的執行範圍中,並且嚴格限制程式碼對本地系統資源訪問,通過這樣的措施來保證對程式碼的有效隔離,防止對本地系統造成破壞。沙箱主要限制系統資源訪問,那系統資源包括什麼?CPU、記憶體、檔案系統、網路。不同級別的沙箱對這些資源訪問的限制也可以不一樣。
所有的java程式執行都可以指定沙箱,可以定製安全策略。在va中將執行程式分成原生代碼和遠端程式碼兩種,原生代碼預設視為可信任的,而遠端程式碼則被看作是不受信的。對於授信的原生代碼,可以訪問一切本地資源。而對於非授信的遠端程式碼在早期的va實現中,安全依賴於沙箱(Sandbox)機制。如下圖所示JDK1.0安全模型
但如此嚴格的安全機制也給程式的功能擴充套件帶來障礙,比如當用戶希望遠端程式碼訪問本地系統的檔案時候,就無法實現。因此在後續的java1.1版本中,針對安全機制做了改進,增加了安全策略,允許使用者指定程式碼對本地資源的訪問許可權。如下圖所示JDK1.1安全模型
在Java1.2版本中,再次改進了安全機制,增加了程式碼簽名。不論原生代碼或是遠端程式碼,都會按照使用者的安全策略設定,由類載入器載入到虛擬機器中許可權不同的執行空間,來實現差異化的程式碼執行許可權控制。如下圖所示JDK1.2安全模型
當前最新的安全機制實現,則引入了域(Domain)的概念。虛擬機器會把所有程式碼載入到不同的系統域和應用域,系統域部分專門負責與關鍵資源進行互動,而各個應用域部分則通過系統域的部分代理來對各種需要的資源進行訪問。虛擬機器中不同的受保護域(Protected Domain),對應不一樣的許可權(Permission)。存在於不同域中的類檔案就具有了當前域的全部許可權,如下圖所示最新的安全模型(jdk1.6)
組成沙箱的基本元件:
- 位元組碼校驗器(bytecode verifier):確保ava類檔案遵循java語言規範。這樣可以幫助java程式實現記憶體保護。但並不是所有的類檔案都會經過位元組碼校驗,比如核心類。
-
類裝載器(class loader):其中類裝載器在3個方面對ava沙箱起作用。
- 它防止惡意程式碼去幹涉善意的程式碼; //雙親委派機制
- 它守護了被信任的類庫邊界;
- 它將程式碼歸入保護域,確定了程式碼可以進行哪些操作。
虛擬機器為不同的類載入器載入的類提供不同的名稱空間,名稱空間由一系列唯一的名稱組成,每一個被裝載的類將有一個名字,這個名稱空間是由java虛擬機器為每一個類裝載器維護的,它們互相之間甚至不可見。 - 類裝載器採用的機制是雙親委派模式。
1.從最內層M自帶類載入器開始載入,外層惡意同名類得不到載入從而無法使用;
2.由於嚴格通過包來區分了訪問域,外層惡意的類通過內建程式碼也無法獲得許可權訪問到內層類,破壞程式碼就自然無法生效。
- 存取控制器(access controller):存取控制器可以控制核心APl對作業系統的存取許可權,而這個控制的策略設定,可以由使用者指定。
- 安全管理器(security manager):是核心APl和作業系統之間的主要介面。實現許可權控制,比存取控制器優先順序高。
-
安全軟體包(security package):java.security下的類和擴充套件包下的類,允許使用者為自己的應用增加新的安全特性,包括:
- 安全提供者
- 訊息摘要
- 數字簽名 Keytools
- 加密
- 鑑別
6、Native
編寫一個多執行緒類啟動
package com.jvm;
public class Demo {
public static void main(String[] args) {
new Thread(() ->{
},"my thread name").start();
}
//native : 凡是帶了native關鍵字的,說明java的作用範圍達不到了,他會去呼叫c語言的庫
//會進入本地方法棧
//呼叫本地方法本地介面 JNI java native interface
//JNI作用:擴充套件java的使用,融合不同的程式語言為Java所用
//java誕生的時候C C++ 橫行,想要立足,必須要有呼叫C C++的程式
//它在記憶體區域專門開闢了一塊標記區域 :Native Method Stack,等級Native方法
//在最終執行的時候,載入本地方法庫中的方法通過JNI
//java程式驅動印表機,管理系統 Robot 掌握即可
private native void s();
//呼叫其他介面: Socket ,WebService~ http~
}
點進去看start方法的原始碼
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
凡是帶了native關鍵字的,說明java的作用範圍達不到,去呼叫底層C語言的庫!
JNI:Java Native Interface (Java本地方法介面)
凡是帶了native:關鍵字的方法就會進入本地方法棧,其他的就是java棧!
Native Interface 本地介面
本地介面的作用是融合不同的程式語言為va所用,它的初衷是融合C/C++程式,Java在誕生的時候是C/C++橫行的時候,想要立足,必須有呼叫C、C++的程式,於是就在記憶體中專門開闢了一塊區域處理標記為native的程式碼,它的具體做法是在Native Method Stack中登記native方法,在(Execution Engine)執行引擎執行的時候載入Native Libraies。
目前該方法使用的越來越少了,除非是與硬體有關的應用,比如通過vā程式驅動印表機或者)ava系統管理生產裝置,在企業級應用中已經比較少見。因為現在的異構領域間通訊很發達,比如可以使用S0ckt通訊,也可以使用Web Service等等,不多做介紹!
Native Method Stack
它的具體做法是Native Method Stack中登記native方法,在(Execution Engine)執行引擎執行的時候載入Native Libraies。【本地庫】
7、PC暫存器
程式計數器:Program Counter Register
每個執行緒都有一個程式計數器,是執行緒私有的,就是一個指標,指向方法區中的方法位元組碼(用來儲存指向像一條指令的地址,也即將要執行的指令程式碼),在執行引擎讀取下一條指令,是一個非常小的記憶體空間,幾乎可以忽略不計
8、方法區
Method Area 方法區
方法區是被所有執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構函式,介面程式碼也在此定義,簡單說,所有定義的方法的資訊都儲存在該區域,此區域屬於共享區間:
靜態變數、常量、類資訊(構造方法、介面定義)、執行時的常量池存在方法區中,但是例項變數存在堆記憶體中,和方法區無關
static final Class模板 常量池
9、棧
1.棧:資料結構
程式 = 資料結構 + 演算法
棧:先進後出、後進先出:桶
佇列:先進先出(FIFO:first input first output)
為什麼main()先執行,最後結束~
棧:棧記憶體,主管程式的執行,生命週期和執行緒同步;
執行緒結束,棧記憶體也就釋放了,對於棧來說,不存在垃圾回收問題
一旦執行緒結束,棧就Over!
棧:8大基本型別 + 物件引用 + 例項的方法
棧執行原理:棧幀
棧滿了:StackOverflowError
棧+堆+方法區:互動關係
藍色堆 粉色方法區 深藍常量池 綠色棧
畫出一個物件例項化的過程在記憶體中:
10、三種jvm
- Sun公司 HotSpot Java HotSpot(TM) 64-Bit Server VM
- BEA
JRockit
- IBM
J9 VM
11、堆
Heap,一個JVM只有一個堆記憶體,堆記憶體的大小是可以調節的。
類載入器讀取了類檔案後,一般會把什麼東西放到堆中? 類,方法,常量,變數~,儲存我們所有引用型別的真實物件
堆記憶體中還要細分為三個區域:
- 新生區(伊甸園區)Young/New
- 養老區 old
- 永久區 Perm
GC垃圾回收,主要是在伊甸園區和養老區~
假設記憶體滿了,OOM,堆記憶體不夠!java.lang.OutOfMemoryError:java heap space
在JDK8以後,永久儲存區有另外一個名字元空間
11.1、新生區
- 類:誕生和成長的地方,甚至死亡;
- 伊甸園,所有的物件都是在伊甸園區new出來的
- 倖存者區(0,1)
老年區
真理:經過研究,99%的物件都是臨時物件!
11.2、永久區
這個區域常駐記憶體。用來存放JDK自身攜帶的Class物件。Interface元資料,儲存的是java執行時的一些環境或類資訊~,這個區域不存在垃圾回收!關閉虛擬機器就會釋放這個區域的記憶體
一個啟動類,載入了大量的第三方jar包。tomcat部署了太多的應用,大量動態生成的反射類。不斷的被載入。知道記憶體滿,就會出現OOM;
- jdk1.6之前:永久代,常量池是在方法區中
- jdk1.7:永久代,但是慢慢的退化了,
去永久代
,常量池在堆中 - jdk1.8之後:無永久代,常量池在元空間
元空間:邏輯上存在:物理上不存在
package com.jvm;
public class Demo02 {
public static void main(String[] args) {
//返回虛擬機器試圖使用的最大記憶體
long maxMemory = Runtime.getRuntime().maxMemory(); //位元組 1024*1024
//返回jvm的總記憶體
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("max="+maxMemory+"位元組\t"+(maxMemory/(double)1024/1024)+"MB");
System.out.println("total="+totalMemory+"位元組\t"+(totalMemory/(double)1024/1024)+"MB");
//預設情況下:分配的總記憶體是電腦記憶體的1/4,初始化的記憶體1/64
//OOM:嘗試擴大堆記憶體空間看結果
// 分析記憶體,看一下那個地方出現了什麼問題
//-Xms1024m -Xmx1024m -XX:+PrintGCDetails
//305664K+699392K
}
}
package com.jvm;
import java.util.Random;
public class Demo03 {
public static void main(String[] args) {
String str = "akjbdjandakjkd";
while (true){
str+=str +new Random().nextInt(888888888)+new Random().nextInt(9999999);
}
//-Xms8m -Xmx8m -XX:+PrintGCDetails
}
}
在一個專案中,突然出現了OOM故障,那麼該如何排除~研究為什麼出錯
- 能夠看到程式碼第幾行出錯:記憶體快照分析工具,MAT,jprofiler
- Dubug,一行行分析程式碼
MAT,jprofiler作用:
- 分析Dump記憶體檔案,快速定位記憶體洩漏;
- 獲得堆中的資料
- 獲得大的物件~
package com.jvm;
import java.util.ArrayList;
//-Xms:設定初始化記憶體分配大小 1/64
//-Xmx:設定最大分配記憶體,預設 1/4
//-XX:+PrintGCDetails //列印GC垃圾回收
//-XX:+HeapDumpOnOutOfMemoryError //oom Dump
//Dump
//-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
public class Demo04 {
byte[] array = new byte[1*1024*1024]; //1m
public static void main(String[] args) {
ArrayList<Demo04> list = new ArrayList<>();
int count = 0;
try {
while (true){
list.add(new Demo04());
count=count+1;
}
}catch (Error e){
System.out.println("count:"+count);
e.printStackTrace();
}
//Throwable
//Exception
//Error
}
}
12、GC:垃圾回收
JVM在進行GC時,並不是對這三個區域統一回收。大部分時候,回收都是新生代
- 新生代
- 倖存區(from,to)
- 老年區
GC兩種型別:輕GC(普通的GC),重GC(全域性GC)
題目:
-
JVM的記憶體模型和分割槽~詳細到每個區放什麼?
JVM 分為堆區和棧區,還有方法區,初始化的物件放在堆裡面,引用放在棧裡面, class 類資訊常量池(static 常量和 static 變數)等放在方法區 new: 方法區:主要是儲存類資訊,常量池(static 常量和 static 變數),編譯後的程式碼(字 節碼)等資料 堆:初始化的物件,成員變數 (那種非 static 的變數),所有的物件例項和陣列都要 在堆上分配 棧:棧的結構是棧幀組成的,呼叫一個方法就壓入一幀,幀上面儲存區域性變量表,操 作數棧,方法出口等資訊,區域性變量表存放的是 8 大基礎型別加上一個應用型別,所 以還是一個指向地址的指標 本地方法棧:主要為 Native 方法服務 程式計數器:記錄當前執行緒執行的行號
-
堆裡面的分割槽有哪些?Eden,from,to,老年區,說說他們的特點
堆中的分割槽主要分為
新生區;它又分為倖存1區和倖存2區,伊甸園區
老年區
首先新生區:大部分的物件都是在伊甸園區建立並回收的,回收後倖存的物件會被放到倖存區(to)區,倖存區1,2他倆是互相交換的,這其中牽涉到了複製演算法, 複製演算法主要被應用在新生區中的倖存區,具體流程就是在倖存區中將活的物件進行復制到另一個倖存區中,留下來的物件就會被清理掉,不斷地交換.這樣做的缺點就是佔用空間大,用到的空間只有開闢的1/2.
複製演算法主要是根據GCroot判斷一個物件是否是垃圾.
當倖存區中經過十五次GC還未被清除掉的物件會進入老年區.
老年代一般存放的是比較大的物件和存活時間很久的物件因此老年代用標記整理演算法,老年代容量滿後會啟用重GC
-
GC的演算法有哪些?標記清除法,標記壓縮,複製演算法,引用計數器,怎麼用?
-
輕GC和重GC分別在什麼時候發生?
Minor GC
此時如果新生的物件無法在 Eden 區建立(Eden 區無法容納) 就會觸發一次Young GC 此時會將 S0 區與Eden 區的物件一起進行可達性分析,找出活躍的物件,將它複製到 S1 區並且將S0區域和 Eden 區的物件給清空,這樣那些不可達的物件進行清除,並且將S0 區 和 S1區交換。
但是這裡會產生一個問題,Q:為啥會有兩個 Survivor 區?
A: 因為假設設想一下只有一個 Survibor 區 那麼就無法實現對於 S0 區的垃圾收集,以及分代年齡的提升。
Major GC
發生在老年代的GC ,基本上發生了一次Major GC 就會發生一次 Minor GC。並且Major GC 的速度往往會比 Minor GC 慢 10 倍。
什麼時候發生Major GC
既然我們已經知道了 Minor GC 是在 Eden 區快滿的情況下才會觸發
Q:那麼 Major GC 呢?
A:
對於一個大物件,我們會首先在Eden 嘗試建立,如果建立不了,就會觸發Minor GC 隨後繼續嘗試在Eden區存放,發現仍然放不下 嘗試直接進入老年代,老年代也放不下 觸發 Major GC 清理老年代的空間 放的下 成功 放不下 OOM
引用計數法:
複製演算法:
- 好處:沒有記憶體的碎片
- 壞處:浪費了記憶體空間:多了一半空間永遠是空的,假設物件100%存活(極端情況)
複製演算法最佳使用場景:物件存活度較低的時候;新生區~
標記清除演算法
- 優點:不需要額外的空間!
- 缺點:兩次掃描嚴重浪費時間,會產生記憶體碎片。
標記壓縮
再優化:
總結
記憶體效率:複製演算法>標記清除演算法>標記壓縮演算法(時間複雜度)
記憶體整齊度:複製演算法=標記壓縮演算法>標記清除演算法
記憶體利用率:標記壓縮演算法=標記清除演算法>複製演算法
GC:分代收集演算法
年輕代:
- 存活率低
- 複製演算法
老年代:
- 存活率高:區域大
- 標記清除演算法(記憶體碎片不是太多)+標記壓縮演算法混合實現
13、JMM
-
什麼是JMM?
JMM:(Java Memory Model的縮寫)
-
它幹嘛的?:官方,其他人的部落格,對應的視訊!
作用:快取一致性協議,用於定義資料讀寫的規則(遵守,找到這個規則)
JMM定義了執行緒工作記憶體和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(Main Memory)中,每個執行緒都有一個私有的本地記憶體(Local Memory)
volilate:解決共享物件可見性這個問題
-
它該如何學習?
JMM:抽象的概念,理論
JMM對這八種指令的使用,制定瞭如下規則:
-
不允許read和load、store和write操作之一單獨出現。即使用了read必須load,使用了store必須write
-
不允許執行緒丟棄他最近的assigr操作,即工作變數的資料改變了之後,必須告知主存
-
不允許一個執行緒將沒有assign的資料從工作記憶體同步回主記憶體
-
一個新的變數必須在主記憶體中誕生,不允許工作記憶體直接使用一個未被初始化的變數。就是懟變數實施use、store操作之前,必須經過assign和load操作
-
一個變數同一時間只有一個執行緒能對其進行lock。多次ock後,必須執行相同次數的unlock.才能解鎖·如果對一個變數進行引0ck操作,會清空所有工作記憶體中此變數的值,在執行引擎使用這個變數前,必須重新load或assign操作初始化變數的值
-
如果一個變數沒有被Iock,就不能對其進行unlock操作。也不能unlock-一個被其他執行緒鎖住的變數
-
對一個變數進行unlock操作之前,必須把此變數同步回主記憶體
JMM對這八種操作規則和對volatile的一些特殊規則就能確定哪裡操作是執行緒安全,哪些操作是執行緒不安全的了。但是這些規則實在複雜,很難在實踐中直接分析。所以一般我們也不會通過上述規則進行分析。更多的時候,使用java的happen-before規側則來進行分析。
-