十、JDK1.8—HashMap以及JVM的優化

JDK1.8以前的HashMap

       原來的hashMap採用的數據結構是哈希表(數組+鏈表),hashMap底層是Entry類型的,名字叫做table的數組,當new一個hsahMap的時候會構造一個容量爲16,負載因子爲0.75的空hashMap,它的吞吐臨界值爲16 * 0.75 = 12。當添加達到吞吐臨界值的時候會用resize的方法2倍擴容,並且通過transfer()將原來的值放入新的Entry數組中
    當執行put命令將key-value鍵值對放入hashMap中的時候,程序首先根據key的hashCode()返回值決定在Entry數組中的存儲位置,如果此位置爲空則直接保存,若不爲空,HashCode肯定相等,就會通過key的equals進行比較,若返回true,新添加的value將覆蓋集合中原來的value,若返回false,新添加的Entry將與集合中原有的Entry形成Entry鏈,而新添加的Entry位於Entry鏈的頭部,在某些情況下如果鏈表 無限下去,那麼效率極低,碰撞是避免不了的 

JDK1.8以後的HashMap

在1.8之後,在數組+鏈表+紅黑樹來實現hashmap,當碰撞的元素個數大於8時 & 總容量大於64,會有紅黑樹的引入 
除了添加之後,效率都比鏈表高,1.8之後鏈表新進元素加到末尾 
ConcurrentHashMap (鎖分段機制),concurrentLevel,jdk1.8採用CAS算法(無鎖算法,不再使用鎖分段),數組+鏈表中也引入了紅黑樹的使用

 

hashCode()與equals()的相關規定:

  1. 如果兩個對象相等,則hashcode一定也是相同的
  2. 如果兩個對象相等,對兩個equals方法返回true
  3. 兩個對象有相同的hashcode值,它們也不一定是相等的
  4. 綜上,equals方法被覆蓋過,則hashCode方法也必須被覆蓋(否則HashMap put數據時,key值無法確定是否爲同一對象)

兩個對象hashCode()相同,equals() 也一定爲 true嗎?


      首先,答案肯定是不一定。同時反過來equals爲true,hashCode也不一定相同。
      類的hashCode方法和equals方法都可以重寫,返回的值完全在於自己定義。
      hashCode()返回該對象的哈希碼值;equals()返回兩個對象是否相等。

關於hashCode和equal是方法是有一些 常規協定 :
      1、兩個對象用equals()比較返回true,那麼兩個對象的hashCode()方法必須返回相同的結果。
      2、兩個對象用equals()比較返回false,不要求hashCode()方法也一定返回不同的值,但最好返回不同值,提搞哈希表性能。
      3、重寫equals()方法,必須重寫hashCode()方法,以保證equals方法相等時兩個對象hashcode返回相同的值。

兩個個關於這兩個方法的重要規範(我只是抽取了最重要的兩個,其實不止兩個): 
	規範1:若重寫equals(Object obj)方法,有必要重寫hashcode()方法,確保通過equals(Object obj)方法判斷結果爲true的兩個對象具備相等的hashcode()返回值。
		說得簡單點就是:“如果兩個對象相同,那麼他們的hashcode應該 相等”。
		不過請注意:這個只是規範,如果你非要寫一個類讓equals(Object obj)返回true而hashcode()返回兩個不相等的值,編譯和運行都是不會報錯的。不過這樣違反了Java規範,程序也就埋下了BUG。 
	規範2:如果equals(Object obj)返回false,即兩個對象“不相同”,並不要求對這兩個對象調用hashcode()方法得到兩個不相同的數。
		說的簡單點就是:“如果兩個對象不相同,他們的hashcode可能相同”。 根據這兩個規範,
		
可以得到如下推論: 
	1、如果兩個對象equals=true,Java運行時環境會認爲他們的hashcode一定相等。 
	2、如果兩個對象不equals=true,他們的hashcode有可能相等。 
	3、如果兩個對象hashcode相等,他們不一定equals=true。 
	4、如果兩個對象hashcode不相等,他們一定不equals=true。 
public class Student {
	private String name;
	private int age;

	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Student() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		Student student = (Student) o;

		if (age != student.age) return false;
		return name.equals(student.name);
	}

	@Override
	public int hashCode() {
		int result = name.hashCode();
		result = 31 * result + age;
		return result;
	}
}

 如果兩個對象equals=true,Java運行時環境會認爲他們的hashcode一定相等。

public static void main(String[] args){
	Student str1 = new Student("dd", 29);
	Student str2 = new Student("dd", 29);
	System.out.println(str1.hashCode());
	System.out.println(str2.hashCode());
	System.out.println(str1.hashCode() == str2.hashCode());
	System.out.println(str1.equals(str2));
}

// 99229
// 99229
// true
// true

 如果兩個對象hashcode相等,他們不一定equals=true。 

public static void main(String[] args){
	Student str1 = new Student("dd1", 29);
	Student str2 = new Student("dd", 2984548);
	System.out.println(str1.hashCode());
	System.out.println(str2.hashCode());
	System.out.println(str1.hashCode() == str2.hashCode());
	System.out.println(str1.equals(str2));
}

// 3076748
// 3083748
// false
// false

1.7之前字符串常量池是放在永久代的,1.7把字符串常量池從永久代中剝離出來,存放在堆空間中。

JDK1.8以後的jvm

JDK1.8 : 1.8同1.7比,最大的差別就是:元數據區取代了永久代。

          元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元數據空間並不在虛擬機中,而是使用本地內存。

         年輕代 : 三分之一的堆空間
              eden區: 8/10 的年輕代空間
              survivor0 : 1/10 的年輕代空間
              survivor1 : 1/10 的年輕代空間
         老年代 : 三分之二的堆空間

如果服務器內存足夠,升級到 JDK 1.8 修改 JVM 參數最簡單的辦法就是將 -XX:PermSize 和 -XX:MaxPermSize 參數替換爲 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize

    1.8中-XX:PermSize 和 -XX:MaxPermSize 已經失效,取而代之的是一個新的區域 —— Metaspace(元數據區)。

    使用JDK1.8以及之後的版本,不會再碰上“java.lang.OutOfMemoryError: PermGen space”這個錯誤了

-XX:MaxMetaspaceSize=128m設置最大的元內存空間128兆

注意:如果不設置JVM將會根據一定的策略自動增加本地元內存空間。
如果你設置的元內存空間過小,你的應用程序可能得到:java.lang.OutOfMemoryError: Metadata space

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章