十、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

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