• 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

  • 相关阅读:
    C#基础(WinForm窗体的单例模式,避免窗体被实例化多次)
    NPOI基础入门(旧版本)
    SQLite数据插入异常
    EClipse开发NDK流程
    git 常用命令
    6.0权限的简单实用
    MVVM模式
    去掉所有字符里面的空格换行符等
    高逼格的实现WiFi共享,不安装第三方wifi共享软件,两种方式实现开启wifi的功能
    常用的正则表达表达式以及简单用法
  • 原文地址:https://www.cnblogs.com/I-will-be-different/p/GC_HashMap.html
Copyright © 2020-2023  润新知