JVM、GC与HashMap

阿里巴巴突然来了个面试邀请电话,问了些java底层的东西,不知所措,所以专门花了些时间做了下学习,顺便记录下,好记性不如烂笔头。

一、对JAVA的垃圾回收机制(GC)的理解

  不同于C/C++需要手工释放对象所占的内存,JAVA全部委托给了GC进行处理,能更有效的防止内存泄漏的情况。一个程序对应着一个JVM,每个JVM会单独有一个堆,java中创建的对象与数组是存放在堆中的,堆中的内存由GC进行管理(栈中存储引用变量、局部变量一级基本数据类型,超出作用域就会立即释放内存)。

  当一个对象没有再被引用时就会被GC标记为可回收状态,然后在一个不确定的时间对其进行回收(一般是在应用程序空闲或者java堆内存不足是被调用),当然在对一个对象进行回收时,首先会执行其finalize()方法,在finalize()方法中你可再次将该对象变为活跃状态,阻止其回收。  

  一个对象还有没有再被引用一般有两种判断方式:

  1、(JDK1.2之前)每个对象都有一个引用计数器,每多一个引用,计数器+1,少一个则-1,当计数器为0时,则表示该对象没有再被引用了。

  2、根搜索算法。这里要盗一张图了:

    

  从GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。  

  java中可作为GC Root的对象有:

    1.虚拟机栈中引用的对象(本地变量表)

    2.方法区中静态属性引用的对象

    3. 方法区中常量引用的对象

    4.本地方法栈中引用的对象(Native对象)

  GC在回收这些空闲对象时会采用标记-复制算法处理:

    

    标记从根节点出发的所有在用对象,然后按顺序复制到一块新内存上,然后再回收旧内存。这样就不会造成造成内存碎片,但是需要一块额外的内存交换空间来进行复制。

 

二、对HashMap的理解

  HashMap结构其实是一个数组+链表的结构。HashMap有一个叫做Entry的内部类,它用来存储key-value对。如下图:

      

static class Entry implements Map.Entry
{
        final K key;
        V value;
        Entry next;
        final int hash;
        ...//More code goes here
}   `

 

    

    创建一个HashMap时会首先创建一个叫做table的Entry数组:

    

    table的默认大小是16。当往map里面存数据时,也就是执行put方法时,首先会调用key的HashCode()方法,然后进行hash计算,计算出其对应在table的index,如果table的index位置上已经有Entry了,首先会判断该Entry的key与要put的key是否相同,如果相同则会替换,如果不同则会根据链表继续判断,即与当前位置的Entry的next指向的下一个Entry的key进行比较,直到遇到相同的key进行替换或者在链表末尾创建一个Entry。

 1 /**
 2   * Associates the specified value with the specified key in this map. If the
 3   * map previously contained a mapping for the key, the old value is
 4   * replaced.
 5   *
 6   * @param key
 7   *            key with which the specified value is to be associated
 8   * @param value
 9   *            value to be associated with the specified key
10   * @return the previous value associated with <tt>key</tt>, or <tt>null</tt>
11   *         if there was no mapping for <tt>key</tt>. (A <tt>null</tt> return
12   *         can also indicate that the map previously associated
13   *         <tt>null</tt> with <tt>key</tt>.)
14   */
15  public V put(K key, V value) {
16   if (key == null)
17    return putForNullKey(value);
18   int hash = hash(key.hashCode());
19   int i = indexFor(hash, table.length);
20   for (Entry<k , V> e = table[i]; e != null; e = e.next) {
21    Object k;
22    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
23     V oldValue = e.value;
24     e.value = value;
25     e.recordAccess(this);
26     return oldValue;
27    }
28   }
29  
30   modCount++;
31   addEntry(hash, key, value, i);
32   return null;
33  }

  从map中取值时也就是执行get()方法也是类似的道理,先计算出key对应table的index,然后在链表上一层一层往下找:

 1 /**
 2   * Returns the value to which the specified key is mapped, or {@code null}
 3   * if this map contains no mapping for the key.
 4   *
 5   * <p>
 6   * More formally, if this map contains a mapping from a key {@code k} to a
 7   * value {@code v} such that {@code (key==null ? k==null :
 8   * key.equals(k))}, then this method returns {@code v}; otherwise it returns
 9   * {@code null}. (There can be at most one such mapping.)
10   *
11   * </p><p>
12   * A return value of {@code null} does not <i>necessarily</i> indicate that
13   * the map contains no mapping for the key; it's also possible that the map
14   * explicitly maps the key to {@code null}. The {@link #containsKey
15   * containsKey} operation may be used to distinguish these two cases.
16   *
17   * @see #put(Object, Object)
18   */
19  public V get(Object key) {
20   if (key == null)
21    return getForNullKey();
22   int hash = hash(key.hashCode());
23   for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
24    Object k;
25    if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
26     return e.value;
27   }
28   return null;
29  }

    备注:

    1、无论你何时实现 equals 方法,你必须同时实现 hashCode 方法。在一个运行的进程中,相等的对象必须要有相同的哈希码。不相等的对象哈希码有可能相同,有同一个哈希值的对象不一定相等。

    2、HashMap有两个参数影响其性能:初始容量加载因子。默认初始容量是16,加载因子是0.75。容量是哈希表中桶(Entry数组)的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。

 

希望大家能一起交流,一起学习!

 

参考文章链接:

http://www.importnew.com/16517.html

http://www.importnew.com/10620.html

http://jbutton.iteye.com/blog/1569746

 

posted @ 2017-05-24 16:39  请叫我胖子  阅读(3877)  评论(2编辑  收藏  举报