JVM 配置
- 程序计数器:当前线程所执行的字节码的行号指示器
- java虚拟机栈:临时变量
- 元空间:类常量池,运行时常量池
- 方法区:类信息,静态变量
- 堆:对象实例,Sting常量池等
类加载过程
加载->链接(验证+准备+解析)->初始化->使用->卸载
加载:将硬盘中的二进制文件转为内存中的class对象
链接:给静态变量赋初始值,符号引用替换为直接引用
- 验证:检查载入的class 文件数据正确性。
- 准备:给类变量(静态变量)分配内存(方法区),直接赋值为最终值。
- 解析:将常量池内的符号引用替换为直接引用。
初始化:执行类变量(静态变量)的赋值和静态语句块
使用:若是第一次创建对象(对象所属的类没有加载到内存中),先执行初始化操作,再堆上为对象分配空间,所有属性设设置默认值,给实例变量赋值,初始化语句,检查是否有父类,有就先执行父类的构造函数。
GC垃圾回收流程
GC垃圾回收,是对堆内存的一清理。
堆内存:
年轻代:(eden[伊甸园],survior[存活区],vlrtual[伸缩区])
老年代:(tenured[旧生代],vlrtual[伸缩区])
永久代:(1.8后就不存在了,换为了元空间)
ArrayList是否线程安全?如何线程安全地操作ArrayList?
ArrayList 是线程不安全的,如果需要线程安全的List,可以从采用Vector/Collections.synchronizedList/CopyOnWriteArrayList
Vector: 使用synchronized关键字
Collections.synchronizedList: 内每一个方法都加了synchronized 关键字
CopyOnWriteArrayList: 在写操作的时候总是要复制,将原来的数据复制到新的数组进行操作,任何可变的操作都是通过ReentrantLock 控制并发。
线程不安全的原因:
当多个线程同时对一个数组进行操作时,如果线程1 执行 list[i] = “a” ,i++;线程2执行 list[i] = “b” ,i++;
如果线程同步执行了list[i] = 的操作,在执行i++,那么i+1 就有可能出现空值,list[i]的值同样可能出现被覆盖的情况。所以说ArrayList 线程不安全。
HashMap、TreeMap、LinkedHashMap的区别?
相同点:都是属于Map,都是通过K-V存储,K不允许重复。都是线程不安全的。
不同点:
项 | HashMap | TreeMap | LinkedHashMap |
---|---|---|---|
按顺序插入存放 | 不支持 | 不支持 | 支持,遍历时按插入的顺序输出 |
按Key排序 | 不支持 | 支持,默认按key升序 | 不支持 |
数据结构 | 数组+链表+红黑树 | 红黑树 | HashMap+双向链表 |
null | key,value都可以为空,但是Key只能有一个为空 | 不允许key,value为空 | key,value都可以为空,但是Key只能有一个为空 |
HashMap为什么线程不安全?如何线程安全地操作?
安全使用Map的三种方法:
1.HashTable,在get/put方法上加上了synchronized关键字,性能很差。
2.Collections.synchronizedMap,所有的方法都加上了synchronized关键字,性能很差。
3.ConcurrentHashMap,每次只给一个桶(数组项)加锁,性能好。
HashMap线程不安全的原因:
1.数据覆盖,
2.读出为null
3.JDK1.7会出现死循环。
ConcurrentHashMap原理?
在JDK1.8后,ConcurrentHashMap采用的是HashMap(数组+链表+红黑树)+synchronized +CAS的设计来实现线程安全。
CAS:在判断数组中当前位置为null时,使用CAS把这个新的节点写入对应数组中的位置。
synchronized:当数组中当前位置不为空时,通过加锁来添加这个节点进入数组(链表<8)或者红黑树(链表>=8)
线程池有哪些参数?
1.corePoolSize:线程池的核心线程数,即便没有任务也会有这么多的线程在等候;
2.maximunPoolSize:最大线程数,超过这个数量会触发拒绝策略。
3.keepAliveTime:线程存活时间,当线程大于corePoolSize时,等到这个时间还没有任务执行的话,线程退出。
4.unit:指定keepAliveTime的单位,如TimeUnit.SECONDS 秒。
5.workQueue:阻塞队列,提交的任务会被放在这个队列里。
6.threadFactory:线程工场,用来创建线程。
7.handler:拒绝策略
ArrayList扩容机制
当我们直接new 一个ArrayList对象时(未指定大小),new 出来的是一个空数组,容量为0;
当第一次调用add 方法时,分配容量,如果是空数组,最小默认容量为10,当需求容量大于此时的容量是,执行扩容方法grow(),扩容为当前容量的1.5倍,然后比较需求容量与扩容后的容量,如果需求容量大于扩容后的容量,那么就将需求容量作为新的容量值,否则去扩容后的容量,然后再进行复制操作,将数据复制到新的数组中。
HashMap数据结构、哈希冲突解决方法?
HashMap其实就是一个大的数组,将Key的HashCode作为数组的下标,value 作为数组的值,当key的hash值冲突时,将新key和旧Key放到链表中。
链表的长度大于8且小于64,会自动扩容,当链表长度大于8且大于64,会自动转为红黑树。如果红黑树的节点数小于6,则将红黑树转为链表。
当发生hash冲突时,通过链地址法将指向下一个entry
HashMap扩容的原理?
HashMap是懒加载,再构建完对象后,没有发生put操作之前,不会初始化和扩容。
当发生首次put时,会调用resize方法进行初始化。初始化的容量为2^4;
当put发现数组大小大于阈值(当前大小的75%)时调用resize方法进行扩容;
扩容大小为当前的大小的2倍,扩容后判断新的容量是否大于最大容量,如果大于最大容量,实际大小为最大容量,最大容量为:2^30。
JVM通常设置哪些参数来调优?
-Xms 初始堆大小
-Xmx 最大堆大小
-Xss 线程栈大小
-XX:NewSize=n 设置年轻代大小
-Xmn 设置年轻代初始大小和最大大小,年轻代增大,老年代就会减小,Sun官方推荐的配置时年轻代占整个堆的3/8
GC信息文章来源:https://www.toymoban.com/news/detail-480486.html
-XX:+PrintGC 打印GC日志
-XX:+PrintGCDetails 打印详细的GC日志信息
-XX:+PrintGCTimeStamps 打印进程启动到现在经历的时间文章来源地址https://www.toymoban.com/news/detail-480486.html
到了这里,关于面试-java常见问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!