天天看點

java inputstream的read一次隻能讀到一個位元組_20210118-JAVA面試題

1、資料庫事務的屬性、隔離級别(MySql -InnoDB)以及隔離級别的實作?

①事務的屬性ACID

a、原子性atomicity:事務是一個不可分割的工作機關。

b、一緻性consistency:事務必須是資料庫從一個一緻性狀态換到另一個一緻性狀态。

c、隔離性isolation:一個事務的執行不受其他事物的幹擾。

d、持久性durability:事務對資料庫的改變是永久的。

②事務的隔離級别

java inputstream的read一次隻能讀到一個位元組_20210118-JAVA面試題

③mysql隔離級别的實作:通過鎖的方式

讀未送出

:性能最好,不加鎖。

串行化

:讀的時候加共享鎖,即其他事務可以并發讀,但是不能寫。寫的時候加排它鎖,其他事務不能并發讀寫。

讀已送出和可重複讀

:MySQL 采用了 MVCC (多版本并發控制) 的方式,在資料庫表中看到的一行記錄可能實際上有多個版本,每個版本的記錄除了有資料本身外,還要有一個表示版本的字段,記為 row trx_id,而這個字段就是使其發生改變的事務的 id,事務 ID 記為 transaction id,它在事務開始的時候向事務系統申請,按時間先後順序遞增。可重複讀是在事務開始的時候生成一個目前事務全局性的快照,而讀已送出則是每次執行語句的時候都重新生成一次快照。

對于一個快照來說,它能夠讀到哪些版本資料,要遵循以下規則:

  1. 目前事務内的更新,可以讀到;
  2. 版本未送出,不能讀到;
  3. 版本已送出,但是卻在快照建立後送出的,不能讀到;
  4. 版本已送出,且是在快照建立前送出的,可以讀到;
2、Springboot事務的傳播機制及實作原理?

事務的傳播機制是針對于嵌套事務而言。@Transactional(propagation = Propagation.REQUIRED)

①REQUIRED

:spring

預設的事務傳播行為

。支援事務。如果方法執行時已經在一個事務中,則加入目前事務,否則重新開啟一個事務,必須在事務中執行。

外層事務送出了,内層才會送出。内/外隻要有報錯,他倆會一起復原

,因為内外層方法在同一個事務中,内層隻要抛出了異常,這個事務就會被設定成rollback-only,即使外層try-catch内層的異常,該事務也會復原。

②REQUIRES_NEW

:支援事務。每次都是建立一個新事務,如果目前已經在事務中了,會挂起目前事務,必須在新的事務中執行。内層事務結束,内層就送出了,不用等着外層一起送出。外層報錯復原,不影響内層。内層報錯復原,外層try-catch内層的異常,外層不會復原。内層報錯復原,然後又會抛出異常,外層如果沒有捕獲處理内層抛出的異常,外層復原。

③NESTED

:支援事務。如果目前已經在一個事務中了,則嵌套在已有的事務中作為一個子事務。如果目前沒在事務中則開啟一個事務。内層事務結束,要等着外層一起送出。外層復原,内層也復原。如果隻是内層復原,不影響外層。這個内層復原不影響外層的特性是有前提的,否則内外都復原。

④SUPPORTS

:支援事務。目前有事務就支援。目前沒有事務就算了,不會開啟一個事物。

⑤MANDATORY

:支援事務,如果業務方法執行時已經在一個事務中,則加入目前事務,否則抛出異常。

⑥NOT_SUPPORTED

:不支援事務,如果業務方法執行時已經在一個事務中,則挂起目前事務,等方法執行完畢後,事務恢複進行。

⑦NEVER

:不支援事務。如果目前已經在一個事務中了,抛出異常。

3、對象在記憶體中的結構布局?

HotSpot虛拟機中,對象在記憶體中存儲的布局可以分為三塊區域:

對象頭(Header)

執行個體資料(Instance Data)

對齊填充(Padding)。

①Mark Word(标記字段):對象的Mark Word部分占4個位元組,其内容是一系列的标記位,比如輕量級鎖的标記位,偏向鎖标記位等等。②Klass Pointer(Class對象指針):Class對象指針的大小也是4個位元組,其指向的位置是對象對應的Class對象(其對應的中繼資料對象)的記憶體位址③對象實際資料:這裡面包括了對象的所有成員變量,其大小由各個成員變量的大小決定,比如:byte和boolean是1個位元組,short和char是2個位元組,int和float是4個位元組,long和double是8個位元組,reference是4個位元組④對齊:最後一部分是對齊填充的位元組,按8個位元組填充。

java inputstream的read一次隻能讀到一個位元組_20210118-JAVA面試題
4、産生OOM的9大原因以及解決方案?

①當堆記憶體(Heap Space)沒有足夠空間存放新建立的對象時,會抛出

java.lang.OutOfMemoryError:Javaheap space

錯誤(根據實際生産經驗,可以對程式日志中的 OutOfMemoryError 配置關鍵字告警,一經發現,立即處理)。

a、請求建立一個

超大對象

,通常是一個大數組,檢視對象的合理性。

b、超出預期的通路量/資料量,通常是上遊系統請求流量飙升,常見于各類促銷/秒殺活動,可以結合

業務流量

名額排查是否有尖狀峰值,添加機器資源或者降流限級。

c、過度使用終結器(Finalizer),該對象沒有立即被 GC。

d、

記憶體洩漏

(Memory Leak),大量對象引用沒有釋放,JVM 無法對其自動回收,常見于使用了 File 等資源沒有回收。

②當 Java 程序花費 98% 以上的時間執行 GC,但隻恢複了不到 2% 的記憶體,且該動作連續重複了 5 次,就會抛出

java.lang.OutOfMemoryError:GC overhead limit exceeded

錯誤。問題的産生原因跟①中的相似。

java.lang.OutOfMemoryError:Permgen space

該錯誤表示永久代(Permanent Generation)已用滿,通常是因為加載的 class 數目太多或體積太大。

a、

程式啟動報錯

,修改 -XX:MaxPermSize 啟動參數,調大永久代空間。

b、應用重新部署時報錯,很可能是

應用重新開機導緻加載了多份 class 資訊

,隻需重新開機 JVM 即可解決。

c、運作時報錯,應用程式可能會

動态建立大量 class

,而這些 class 的生命周期很短暫,但是 JVM 預設不會解除安裝 class,可以設定 -XX:+CMSClassUnloadingEnabled 和 -XX:+UseConcMarkSweepGC這兩個參數允許 JVM 解除安裝 class。

d、如果上述方法無法解決,可以通過 jmap 指令 dump 記憶體對象 jmap-dump:format=b,file=dump.hprof<process-id> ,然後利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析開銷最大的 classloader 和重複 class。

④JDK 1.8 使用

Metaspace

替換了永久代(Permanent Generation),該錯誤表示 Metaspace 已被用滿,通常是因為加載的 class 數目太多或體積太大。此類問題的原因與解決方法跟 Permgenspace 非常類似,需要特别注意的是調整 Metaspace 空間大小的啟動參數為 -XX:MaxMetaspaceSize。

java.lang.OutOfMemoryError:Unableto createnewnativethread :

每個 Java 線程都需要占用一定的記憶體空間,當 JVM 向底層作業系統請求建立一個新的 native 線程時,如果沒有足夠的資源配置設定就會報此類錯誤。

a、線程數

超過作業系統最大線程數

ulimit 限制;

b、線程數超過 kernel.pid_max(隻能重新開機);

c、

native 記憶體不足

Out of swap space

:該錯誤表示所有可用的虛拟記憶體已被耗盡。虛拟記憶體(Virtual Memory)由實體記憶體(Physical Memory)和交換空間(Swap Space)兩部分組成,當運作時程式請求的虛拟記憶體溢出時就會報 Outof swap space錯誤。

a、

位址空間

不足;

b、

實體記憶體

已耗光;

c、應用程式的

本地記憶體洩漏(native leak)

,例如不斷申請本地記憶體,卻不釋放。

Kill process or sacrifice child

:有一種核心作業(Kernel Job)名為 Out of Memory Killer,它會在可用記憶體極低的情況下“殺死”(kill)某些程序。OOM Killer 會對所有程序進行打分,然後将評分較低的程序“殺死”,具體的評分規則可以參考 Surviving the Linux OOM Killer。不同于其他的 OOM 錯誤, Killprocessorsacrifice child 錯誤不是由 JVM 層面觸發的,而是由作業系統層面觸發的。

Requested array size exceeds VM limit

JVM 限制了數組的最大長度

,該錯誤表示程式請求建立的數組超過最大長度限制。JVM 在為數組配置設定記憶體前,會檢查要配置設定的資料結構在系統中是否可尋址,通常為

Integer.MAX_VALUE-2

此類問題比較罕見,通常需要檢查代碼,确認業務是否需要建立如此大的數組,是否可以拆分為多個塊,分批執行。

Direct buffer memory

:Java 允許應用程式通過 Direct ByteBuffer 直接通路堆外記憶體,許多高性能程式通過 Direct ByteBuffer 結合記憶體映射檔案(Memory Mapped File)實作高速 IO。Direct ByteBuffer 的預設大小為 64 MB,一旦使用超出限制,就會抛出 Directbuffer memory 錯誤。

5、Finalizer的運作過程是什麼?為什麼過度使用Finalizer會出現OOM錯誤?

①JVM建立實作了finalize()方法的類的對象

②JVM會建立java.lang.ref.Finalizer執行個體,指向①中建立的對象

③垃圾回收器首先會把①中對象添加到java.lang.ref.Finalizer.ReferenceQuene隊列中,Finalizer線程會處理這個隊列,将裡面的對象逐個彈出,并調用他們的finalize()方法。

④finalize()方法調用完後,Finalizer線程會将引用從Finalizer執行個體中去掉,在下一輪GC中,①中的對象可以被回收。

⑤Finalizer線程會與main線程進行競争,由于他的優先級較低,獲得的cpu資源較少,是以永遠趕不上主線程,是以會導緻程式消耗所有的可用資源,導緻OOM錯誤。