學習筆記(每天持續更新)

數據結構

B樹(B-樹)和B+樹

https://my.oschina.net/u/4116286/blog/3107389

紅黑樹

二叉查找樹:
左子樹值均小於根節點,右子樹均大於根節點
左右子樹均爲二叉排序樹(二分查找的思想)

二叉查找樹的缺點:
不斷地插入遞增或者遞減的值,可能會變成線性表

即將二叉查找樹變成一種平衡樹

紅黑樹就是一種策略,也就是說紅黑樹是一種平衡二叉查找樹

紅黑樹特點:

  1. 節點是紅色或者黑色
  2. 根節點是黑色
  3. 每個葉子的節點都是黑色的空節點(NULL)
  4. 每個紅色節點的兩個子節點都是黑色的。
  5. 從任意節點到其每個葉子的所有路徑都包含相同的黑色節點。

....變色和旋轉,理解不動啊。。。

數據庫

索引的失效情況

  1. 條件中有or,無論什麼索引都會失效
    • 可以用in
    • 使用or,又想索引生效,只能將or條件中的每個列都加上索引
  2. 複合索引中需要遵循最左前綴
    • 比如有(A,B,C)索引,索引生效的情況有A,AB,ABC
    • 如果查詢中有AC這樣,那麼只有A索引條件生效
  3. 範圍查詢後面的查詢,索引不成效
    • A=x,B>x,C=x,C不生效
  4. 在索引上進行運算,索引失效
  5. 字符串沒有單引號,索引失效
  6. 儘量使用覆蓋索引(只訪問索引的查詢(索引列完全包含查詢列)),減少select * 。查詢列,超出索引列,也會降低性能。
  7. 以%開頭的Like模糊查詢,索引失效。
    • 如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效。
  8. in 走索引, not in 索引失效。
  9. 儘量使用複合索引,而少使用單列索引
    • 創建複合索引(A,B,C),相當於創建了A,A+B,A+B+C

索引的結構,這裏說的是B+樹索引

https://blog.csdn.net/weixin_30531261/article/details/79312676

  • 索引爲什麼不能是鏈表結構

假設有name索引,在我們找到第一個name屬性爲“Jesus”的節點後,繼續往後找,當遇到name屬性不爲“Jesus”的節點時,就無需再往後查找了,因爲節點是根據name屬性有序排列的啊。假設第一個name=“Jesus”的節點是第499個節點,最後一個name=“Jesus”的節點是第500個節點,那麼只需要遍歷501個節點就可以了。當發現第501個節點的name字段不爲“Jesus”,後面的499個節點也就無需遍歷了。

但是這樣的話前500次查詢是很慢的,效率太低了。

  • 索引爲什麼不能是平衡二叉樹
    平衡樹的查找時間複雜度是O(log2N),已經很不錯了

索引是存在於索引文件中,是存在於磁盤中的。因爲索引通常是很大的,因此無法一次將全部索引加載到內存當中,因此每次只能從磁盤中讀取一個磁盤頁的數據到內存中。而這個磁盤的讀取的速度較內存中的讀取速度而言是差了好幾個級別。

注意,我們說的平衡二叉樹結構,指的是邏輯結構上的平衡二叉樹,其物理實現是數組。然後由於在邏輯結構上相近的節點在物理結構上可能會差很遠。因此,每次讀取的磁盤頁的數據中有許多是用不上的。因此,查找過程中要進行許多次的磁盤讀取操作。

而適合作爲索引的結構應該是儘可能少的執行磁盤IO操作,因爲執行磁盤IO操作非常的耗時。因此,平衡二叉樹並不適合作爲索引結構。

  • 爲什麼B樹適合做索引
    B樹是爲了充分利用磁盤預讀功能來而創建的一種數據結構
    • 磁盤的局部性原理:
      磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向後讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。
      程序運行期間所需要的數據通常比較集中。由於磁盤順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有局部性的程序來說,預讀可以提高I/O效率。

B樹的每個節點可以存儲多個關鍵字,它將節點大小設置爲磁盤頁的大小,充分利用了磁盤預讀的功能。每次讀取磁盤頁時就會讀取一整個節點。也正因每個節點存儲着非常多個關鍵字,樹的深度就會非常的小。進而要執行的磁盤讀取操作次數就會非常少,更多的是在內存中對讀取進來的數據進行查找。

B樹的查詢,主要發生在內存中,而平衡二叉樹的查詢,則是發生在磁盤讀取中。因此,雖然B樹查詢查詢的次數不比平衡二叉樹的次數少,但是相比起磁盤IO速度,內存中比較的耗時就可以忽略不計了。因此,B樹更適合作爲索引。

  • 更加適合做索引的B+樹

B+樹的關鍵字全部存放在葉子節點中,非葉子節點用來做索引,而葉子節點中有一個指針指向一下個葉子節點。做這個優化的目的是爲了提高區間訪問的性能。而正是這個特性決定了B+樹更適合用來存儲外部數據。

JVM

Minor Gc 和 Full GC 有什麼不同呢?

目前主流的垃圾收集器都會採用分代回收算法,因此需要將堆內存分爲新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。
新生代 GC(Minor GC): 指發生新生代的的垃圾收集動作,Minor GC 非常頻繁,回收速度一般也比較快。
老年代 GC(Major GC/Full GC): 指發生在老年代的 GC,出現了 Major GC 經常會伴隨至少一次的 Minor GC(並非絕對),Major GC 的速度一般會比 Minor GC 的慢 10 倍以上。

什麼對象會進入老年代

大對象直接進入老年代
大對象就是需要大量連續內存空間的對象(比如:字符串、數組)
長期存活的對象將進入老年代
既然虛擬機採用了分代收集的思想來管理內存,那麼內存回收時就必須能識別哪些對象應放在新生代,哪些對象應放在老年代中。爲了做到這一點,虛擬機給每個對象一個對象年齡(Age)計數器。
如果對象在 Eden 出生並經過第一次 Minor GC 後仍然能夠存活,並且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將對象年齡設爲 1. 對象在 Survivor 中每熬過一次 MinorGC, 年齡就增加 1 歲,當它的年齡增加到一定程度(默認爲 15 歲),就會被晉升到老年代中。

對象的引用的分類(四種)

  1. 強引用
    以前我們使用的大部分引用實際上都是強引用,這是使用最普遍的引用。如果一個對象具有強引用,那就類似於必不可少的生活用品,垃圾回收器絕不會回收它。當內存空 間不足,Java 虛擬機寧願拋出 OutOfMemoryError 錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足問題。

  2. 軟引用(SoftReference)
    如果一個對象只具有軟引用,那就類似於可有可無的生活用品。如果內存空間足夠,垃圾回收器就不會回收它,如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。
    軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收,JAVA 虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。

  3. 弱引用(WeakReference)
    如果一個對象只具有弱引用,那就類似於可有可無的生活用品。弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它 所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程, 因此不一定會很快發現那些只具有弱引用的對象。
    弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java 虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

  4. 虛引用(PhantomReference)
    “虛引用” 顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。
    虛引用主要用來跟蹤對象被垃圾回收的活動。

  5. 虛引用與軟引用和弱引用的一個區別:
    虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃 圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是 否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。
    特別注意,在程序設計中一般很少使用弱引用與虛引用,使用軟引用的情況較多,這是因爲軟引用可以加速 JVM 對垃圾內存的回收速度,可以維護系統的運行安全,防止內存溢出(OutOfMemory)等問題的產生。

如何判斷一個類是無用的類

方法區主要回收的是無用的類,那麼如何判斷一個類是無用的類的呢?
判定一個常量是否是 “廢棄常量” 比較簡單,而要判定一個類是否是 “無用的類” 的條件則相對苛刻許多。類需要同時滿足下面 3 個條件才能算是 “無用的類” :

  1. 該類所有的實例都已經被回收,也就是 Java 堆中不存在該類的任何實例。
  2. 加載該類的 ClassLoader 已經被回收。
  3. 該類對應的 java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

虛擬機可以對滿足上述 3 個條件的無用類進行回收,這裏說的僅僅是 “可以”,而並不是和對象一樣不使用了就會必然被回收。

如何判斷一個常量是廢棄常量

運行時常量池主要回收的是廢棄的常量。那麼,我們如何判斷一個常量是廢棄常量呢?
假如在常量池中存在字符串 “abc”,如果當前沒有任何 String 對象引用該字符串常量的話,就說明常量 “abc” 就是廢棄常量,如果這時發生內存回收的話而且有必要的話,“abc” 就會被系統清理出常量池。
JDK1.7 及之後版本的 JVM 已經將運行時常量池從方法區中移了出來,在 Java 堆(Heap)中開闢了一塊區域存放運行時常量池。

如何判斷對象是否死亡(兩種方法)

  1. 引用計數法
    給對象中添加一個引用計數器,每當有一個地方引用它,計數器就加 1;當引用失效,計數器就減 1;任何時候計數器爲 0 的對象就是不可能再被使用的。
    這個方法實現簡單,效率高,但是目前主流的虛擬機中並沒有選擇這個算法來管理內存,其最主要的原因是它很難解決對象之間相互循環引用的問題。 所謂對象之間的相互引用問題,假如除了對象 objA 和 objB 相互引用着對方之外,這兩個對象之間再無任何引用。但是他們因爲互相引用對方,導致它們的引用計數器都不爲 0,於是引用計數算法無法通知 GC 回收器回收他們。

  2. 可達性分析算法
    這個算法的基本思想就是通過一系列的稱爲 “GC Roots” 的對象作爲起點,從這些節點開始向下搜索,節點所走過的路徑稱爲引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連的話,則證明此對象是不可用的。

注意:不可達的對象並非 “非死不可”

即使在可達性分析法中不可達的對象,也並非是 “非死不可” 的,這時候它們暫時處於 “緩刑階段”,要真正宣告一個對象死亡,至少要經歷兩次標記過程;

  1. 可達性分析法中不可達的對象被第一次標記並且進行一次篩選,篩選的條件是此對象是否有必要執行 finalize 方法。當對象沒有覆蓋 finalize 方法,或 finalize 方法已經被虛擬機調用過時,虛擬機將這兩種情況視爲沒有必要執行。
  2. 被判定爲需要執行的對象將會被放在一個隊列中進行第二次標記,除非這個對象與引用鏈上的任何一個對象建立關聯,否則就會被真的回收。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章