dsg_02hashcode原理与jvm内部结构

list集合

ArrayList

ArrayList底层是数组,它是不断的扩容的,当你增加了新的数据超过了他所设定的容量,他会创建一个更大的新的数组,将之前的数组的数据复制到这个新的数组中去,再添加新的元素

LinkedList

LinkedList底层是链表,链表是没有容量的概念的

总结

对于大批量写操作,用LinkedList实现比较快,因为它不需要扩容;对于大批量的读操作,用ArrayList实现比较快,因为它可以使用下标直接定位到某个元素

Hashcode

HashMap集合

HashMap集合集中ArrayList和LinkedList的优点,既有查询快,也有修改添加快的优点。
HashMap的底层是Node数组,Node数组里面存放的是Node对象,Node对象里面有如下属性:

 static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;	//hash值
        final K key;	//key
        V value;	//value
        Node<K,V> next;	//指向下一个Node对象的引用
 }

HashMap如何保证key的值唯一:

	//传入key的新hash值,key,value
	public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
	
	//用key的hash值往右移动,再让keyhash值低的16位异或高16位得到新hash值
	static final int hash(Object key) {
        int h;
        //hashCode方法是Object类的方法,每一个类都是默认继承Object类的,可以直接调用,hash值取的内存地址 
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
	
	
	final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //首先判断map的底层Node数组是否为空,如果是空的就让他重新分配数组的容量 
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //根据key传过来的(key的新hash)异或(Node数组的容量 - 1)得到这个数据在Node数组中的位置,并且判断这个位置有没有数据
        if ((p = tab[i = (n - 1) & hash]) == null)
        	//如果没有,就将这个数据放到这个位置上
            tab[i] = newNode(hash, key, value, null);
        else {
        	//如果有,就判断这个位置上数据的hash跟传递过来的hash是否相等并且(这个位置上的key等不等与传递过来的key的内存地址或者这个位置上的key等不等与传递过来的key的内容,所以当Map的key是自己定义的对象类型的时候,是需要重写 equals方法的)
            Node<K,V> e; K k;
            //判断key相等的语句
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //相等就覆盖原来的值
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
	
Hashset集合

HashSet集合是线程不安全的,存取速度快,HashSet判断数据不重复底层是通过HashMap的key来实现的

JVM的分区

JVM分为堆区和栈区

栈区

栈区是方法存储区,调用方法的执行叫压栈,如果调用的方法太多,会引起栈溢出报错,递归调用就很容易引起栈溢出,报java.lang.StackOverflowError的错误
模拟栈的溢出:

	public static void main(String[] args) {
        callSelf(0);
    }

    public static void callSelf(int n){
        System.out.println(n++);
        callSelf(n);
    }
idea编译器调整jvm虚拟机栈的大小的步骤:
  • run -> run
    在这里插入图片描述
  • 选择你要运行的程序,点击小箭头,选择edit编辑
    在这里插入图片描述
  • 选择VM options那一行,输入命令 -Xss加上需要分配给栈的内存
    在这里插入图片描述
堆区

堆区是虚拟机用来存放对象和数组的地方,也是垃圾回收的主要区域

查看程序垃圾回收机制
  1. 在cmd命令中输入以下命令打开虚拟机查看工具
    	jvisualvm
    
  2. 点击工具 -->插件 --> 可用插件 --> visual gc --> 安装
  3. 然后回到应用程序,双击org.jetbrains.idea.maven.server.RemoteMavenServer,点击visual gc 即可查看idea运行的java代码在jvm内存运行情况

堆区的划分:

  • 堆:
    • 年轻代:伊甸区 + 幸存一区 + 幸存二区
    • 年老代
  • 非堆:(jdk1.8之前叫永久区,jdk1.8之后叫Metaspace)
JVM内存堆区的划分

在这里插入图片描述

  • memory:内存
  • off-heap:离堆,JVM之外的内存,操作系统的内存
  • non-heap:非堆,虚拟机里面堆面的空间,主要存放方法的区域
  • heap:堆
  • old gen:年老代
  • young gen:年轻代
  • eden:伊甸区,对象的创建所在区
  • s0:幸存一区
  • s1:幸存二区

总结: JVM垃圾回收器会先去伊甸区回收,如果伊甸区回收不掉再去幸存一区和幸存二区回收,如果幸存区回收不掉会再去年老代回收,如果年老代回收不掉程序就会报错,堆溢出

jVM调参

  1. 打开黑窗口,输入java,有一个输出非标选项的帮助
    在这里插入图片描述
  2. 输入 java -X,里面有设置堆大小,栈大小的命令语句
    在这里插入图片描述
    另外还有一些更细的参数设置:
-Xms堆大小设置,初始值默认为物理内存的1/64
-Xmx堆最大值设置
-Xmn年轻代大小
-XX:NewSize=n设置年轻代大小
-XX:NewRatio=n设置年轻代和年老代的比例,例如3,年轻代比年老代为1:3
-XX: SurvivorRatio=n设置伊甸区相对单个幸存区的倍数,伊甸区要大于等于单个幸存区
-XX:MetaspaceSize=n设置非堆的大小

类加载

加载过的类不会再加载,即便是使用Class.forName也是一样的,不会重复加载,类中的静态代码块,在类加载的时候会也会被加载,所以说静态代码块只会被执行一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值