文章目录

东篱南山

采菊东篱下,悠然现南山

X

面试篇--Java基础

1.基础

1.1数组为啥能[i]取值?
  数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据
1.2 基本数据类型
  byte、short、int、double、long、float、boolean、char
1.3.String的HashCode
String 是char[]数组组成
  String 的 char 数组的数字每次乘以 31 再叠加最后返回
  31 有个很好的性能,可以得到更好的性能  31 * i == (i << 5) - i
1.4 hashcode和equals实现?这么做有何优劣。
1.5 error和exception的区别,CheckedException,RuntimeException的区别。
 error和exception的均继承Throwable  Error(错误)是系统中的错误,程序是不能改变的和处理的 异常是程序可能出现的问题
 runtimeException不用显示try()catch() 空指针 数据越界 类型转换 IO异常 数据库异常
1.6 什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
       Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
1.7 java8的新特性。
 Lambda表达式和函数式接口
 Stream
1.8 反射

2.集合

2.1 List
   2.1.1 ArrayList
     add() 在数组尾部增加元素,但是数组增加或者删除中间元素会引起元素移动
	 扩容() 原容量右移两位+原容量 相当于扩了1.5倍,扩容完成由Array.copyof()和System.copyOf()将原数组拷贝到新数组上
	 实现了RandomAcces()接口,同时因为是数组,所以支持随机访问
	 因为for循环删除会导致元素移动,然后会删除不干净
	iterator()中三个方法  hasNext()  next(),remove() 操作完成会有指针的变化
	
    2.1.2 LinkList
	底层是链表实现,链表查询改变前后节点引用,不涉及元素移动,所以插入快,查找满
	
	以上均为不安全的集合
   
    2.1.4 CopyOnWriteList
		ArrayList线程安全版本,适合多读少写场景,每当有写发生时,会将当前List产生一个新的副本,再上边操作,然后再该改变引用。	
2.2 Map
   2.2.1 HashMap
          1.7:  hashCode&(table.length-1)
          1.8:  hashCode^(hashCode>>>16)  
            为啥数组长度是2的整次幂?长度减少1 正好相当于一个低位掩码,与运算高位归0,保留低位,且比取模有更高的效率
          右移16位,正好是位数一半,混合原始hash码的高低位,以此加大低位随机性
          put()
		  1.先判断table是不是空的,如果是扩容
		  2.根据hashCode找桶,找到桶,如果节点为空,直接存入
		  3.如果不为空,equals(),然后覆盖
		  4.如果是树,插入树节点
		  5.如果是链表,遍历链表插入,深度如果到8,转为树
		  6.判断是不是到达负载值
          get()
		  跟put()相反
          resize()
		     transfer()将旧数组元素迁往新数组
			1.7 重新机算hash,旧链表迁新链表,如果在新表位置相同,则元素会倒置,1.8不会
			
		jdk1.8之前并发操作hashmap时为什么会有死循环的问题	
		    假如两个操作都触发扩容操作,扩容里边的transfer()方法,会造成节点的相互引用,然后再get的到造成一直遍历,
引申:
Hash求法:
1.直接寻址法
2.数组分析法
3.平方取中法
4.除留取余法
5.随机数法
Hash冲突处理:
	1.开放地址法 以发生冲突的哈希地址为输入,通过某种哈希冲突函数得到一个新的空闲的哈希地址的方法。
	2.创建一个公共溢出区
	3.再哈希法
	4.拉链法

     2.2.2 HashSet
   
     2.2.3 treeMap
       key必须实现comprable接口,
   2.2.4 LinkedHashMap了解基本原理、哪两种有序、如何用它实现LRU
         其实 LinkedHashMap 几乎和 HashMap 一样:
		  其中多了三个参数,头节点、尾节点和顺序布尔值,维护了一个链表,每当有操作时候,会把操作节点移动到尾节点上,慢慢的头节点就是最少使用的节点的,当缓存满时,就可移除
		  两种顺序,插入顺序和访问顺序
		 
   2.2.5 ConcurrentHashMap
  	     1.7 分段锁 由数组结构锁定段 因为初始为16个,所以并发也就16 
		 1.8 去掉分段所,CAS+Volatile+Native方法锁定树或者数组头节点
fail-fast和fail-safe
fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。
    fail-fast会在以下两种情况下抛出ConcurrentModificationException
     (1)单线程环境
         集合被创建后,在遍历它的过程中修改了结构。
         注意 remove()方法会让expectModcount和modcount 相等,所以是不会抛出这个异常。
	  (2)多线程环境
         当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。

    fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException
    fail-safe机制有两个问题
    (1)需要复制集合,产生大量的无效对象,开销大
    (2)无法保证读取的数据是目前原始数据结构中的数据。
实现原理:
   迭代器内部会维护一个标识,当集合结构改变,标识会被修改,而迭代器每次的hasNext()和next()方法都会检查该"mode"是否被改变,当检测到被修改时,抛出Concurrent Modification Exception

QUEUE
LinkedBlockingQueue的容量是没有上限的(说的不准确,在不指定时容量为Integer.MAX_VALUE,不要然的话在put时怎么会受阻呢),但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。

 ArrayBlockingQueue在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来 达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队 列,此队列按 FIFO(先进先出)原则对元素进行排序。
 
 PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对 PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞 队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元 素要具有比较能力。
 
 最后,DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。

JAVA中注解

3.关键字

final
static
private
Iterator
关于Iterator主要有三个方法:hasNext()、next()、remove()
hasNext:没有指针下移操作,只是判断是否存在下一个元素
next:指针下移,返回该指针所指向的元素
remove:删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素

4.内存模型

JVM内存模型 主要目标就是定义了程序中各个变量的访问规则,也就是虚拟机中变量存储到内存和从内存取出变量的底层细节

重排序 指令重排是指在程序执行过程中, 为了性能考虑, 编译器和CPU可能会对指令重新排序.
内存屏障是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作
happen-before
单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。
锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。
volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作(当然也包括写操作了)。
happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。

5.字节码

 cofe babe 是 魔数
 接着两位是 次版本号
 继续两位是 主版本号

6.类加载

6.1 类载流程
  加载---验证---准备---解析--- 初始化----使用----卸载
  简单说说你了解的类加载器,
  启动类加载器(加载\lib),拓展类加载器(\lib\ext),应用类加载器(calssPath),自定义类加载器
  双亲委派是什么?
  就是说儿子收到类加载请求,自己不干,先找父类加载,父类能加载就返回,不能加载自己再来加载
  为什么要双亲委派?
  避免类的重复加载,核心API不容易被篡改
6.2 如何破坏双亲委派?
6.3 为什么要破坏双亲委派?
 JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口
双亲委派模型并不是一个强制性约束,而是java设计者推荐给开发者的类加载器的实现方式,在一定条件下,为了完成某些操作,可以“破坏”模型。     JNDI服务,JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK1.3时就放进rt.jar),但它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI, Service Provider Interface)的代码,但启动类加载器不可能“认识“这些代码啊。因为这些类不在rt.jar中,但是启动类加载器又需要加载,所以引入了线程上下文,有了线程上下文加载器,JNDI服务使用这个线程上下文加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则。
6.4 ClassNotFoundException和NoClassDefFoundErr区别?
  1.文件未被找到   2.在程序利用反射时候,就可能碰到这种异常,一般因为找不到对应类
6.5 如何解决ClassNotFound?
6.6 修改类加载策略要复写哪个方法?

7.并发

7.1 Synchronized
   1. 普通同步方法,锁是当前实例对象
   2. 静态同步方法,锁是当前类的class对象
   3. 同步方法块,锁是括号里面的对象
   原理:Synchronized 同步代码块 monitorenter和monitorexit指令分别对应synchronized同步块的进入和退出
   同步方法则是加了一个ACC_SYNCHRONIZED标识,在JVM进行方法调用时,发现调用的方法被ACC_SYNCHRONIZED修饰,则会先尝试获得锁。
  synchronized的底层是使用操作系统的mutexlock实现的,Java线程映射到操作系统层面,无论唤醒还是阻塞都需要操作系统支持,从用户态转为核心态,所以是个重量级操作
7.2Lock
    7.2.1 乐观锁和悲观锁 
	    悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
        乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。
7.3 并发容器
     CopyOnWriteList
	 ConcurrentHashMap
	 
	 为什么java.util.concurrent 包里没有并发的ArrayList实现?
	    很难去开发一个通用并且没有并发瓶颈的线程安全的List。
		  拿contains() 这样一个操作来说,当你进行搜索的时候如何避免锁住整个list?
7.4 volatile的原理
    修饰关键字,保证了可见性,不能保证原子性,使用Volatile关键字,汇编上会在变量前边增加Lock前缀,然后会导致两种效果:
	   1.当前处理器缓存行会写回到主存,2.写回操作会让其他CPU上该数据无效化
7.5 Lock与Synchronized的区别 。
7.6 自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁。
	自旋锁:线程获取锁的时候,如果锁被其他线程持有,则当前线程将循环等待,直到获取到锁。
    偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
    轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
    可重入锁 在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁
    公平锁是指多个线程按照申请锁的顺序来获取锁。
7.7 Java对象头  
	Hotspot的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针),如果是数组还有数组长度
	 markWord 存储对象自身的运行时数据,主要存储HashCode、分代年龄和锁标记位等消息
7.8 原子类
   7.8.1 CAS+volatile+Unsafe类获取内存上的值  循环比较 如果相同则成功,不同则继续尝试
   7.8.2 产生ABA问题----》1.增加版本号 2.JDK增加了类AtomicStampedReferenc,这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
   循环时间长开销大-----》CAS不成功,则会原地自旋,如果长时间自旋会给CPU带来非常大的执行开销    
7.9 AQS
    7.9.1 重要的字段和方法
        private volatile int state;    
		其中包含了5个状态:
		1.CANCELLED:线程被取消。(非负值)
        -1.SIGNAL:等待触发,只有当前节点的前驱节点状态为SIGNAL,当前节点的线程才可以被挂起。
        -2.CONDITION:等待条件状态。
        -3.PROPAGATE:状态需要向后传播。
        0:普通的同步器初始化为0。
 			字段为同步状态,其中 state > 0 为有锁状态,每次加锁就在原有 state 基础上加 1,即代表当前持有锁的线程加了 state 次锁,反之解锁时每次减一,当 statte = 0 为无锁状态;
	 7.9.2 AQS 的结构
		用 volatile 修饰的整数类型的 state 状态,用于表示同步状态,提供 getState 和 setState 来操作同步状态;
        提供了一个 FIFO 等待队列,实现线程间的竞争和等待,这是 AQS 的核心;
        AQS 内部提供了各种基于 CAS 原子操作方法,如 compareAndSetState 方法,并且提供了锁操作的acquire和release方法。
		排他锁:
		1.当一个线程来了,设置state为1,如果成功,返回,如果不成功,通过CAS操作将其放入队列尾节点上
        2.在队列中时,如果前置节点是头节点,在队列中尝试获取资源,获取失败后判断是否真正需要进入阻塞状态,如果是将阻塞线程,直到被唤醒,并返回中断状态。不断循环,直到获取到资源或者进入阻塞状态等待被唤醒
		3.获取资源失败后判断线程是否需要真正进入阻塞,只有在前驱节点waitStatus值为SIGNAL,当前节点的线程才需要进入阻塞
		4.在这里线程进入了阻塞,调用 LockSupport.park(this)阻塞线程
		
		
		共享锁	1.调用tryRelease(),如果成功,唤醒后继节点,唤醒后,设置节点状态为0,允许失败,一般来说需要唤醒的就是下一个节点,但是下一个节点可能是null,或者其状态是取消状态,所以从tail开始先前查找,一直找到状态正常的节点。
		2.  //唤醒线程
        LockSupport.unpark(s.thread);

	附:https://brightloong.github.io/2018/06/21/Java%E5%90%8C%E6%AD%A5%E5%99%A8%E2%80%94%E2%80%94AQS%E5%AD%A6%E4%B9%A0/
7.10 线程池
	7.10.1 线程池的种类
	   定长,缓存,单例和调度
	7.10.2 线程池作用
	   实现线程管理,线程复用和控制并发数
	7.10.3 线程池核心参数
	    coreSize,MaxSize,time,工作队列,线程工厂(为线程设置名字,异常处理等),Handler(任务提交失败处理策略)
	7.10.4 线程池的状态
	       Running 运行状态
		   shutdown 不再接受任务,但会继续处理任务
		   stop 不再接受任务,也会中断所有任务
		   tidying 任务执行完成,会进行回调
		   terminated 回调完成
		 
	7.10.5 线程池工作流程
	       1.提交任务,如果小于coreSize,就增加worker,
		   2.继续提交,大于coreSize小于MaxSize,就放入队列,
		   3.继续提交,如果队列满了,就开始创建非核心线程处理任务,
		   4.还继续提交,线程到达maxsize,就开始执行拒绝策略
	7.10.6 线程池实现复用原理
	       线程池中有个volatile修饰的值来记录线程池中线程池状态和线程数
		   其中高三位记录线程池状态,低29位记录线程数,
		   复用原理:
		     1.线程池中任务执行者是worker(),worker保持着while()循环
			 2.如果从队列中没有拿到任务就阻塞,拿到任务就执行提交任务中的run()
			 3.当大于CoreSize()时候就创建非核心线程来执行任务,完事了线程执行完了就完了
	7.10.7 为啥不推荐使用Executors?
	        ExecutorS创建线程池很多参数都是设定好的,而且有些值的设定(队列长度位Inter.max()),会导致OOM。
	7.10.8 执行ececute()和Submit()区别
			ececute()提交不需要返回值的线程
			submit()提交需要返回值的线程,返回结果future对象,可用get()来获取结果,但该操作会阻塞,推荐使用带时间的get()
	7.10.9 根据什么来确定线程池核心线程数?
		    根据业务来分,机算密集型 线程数就是CPU个数+1
			               IO密集型   线程数就是n*CPU个数
     7.10.10 线程池执行的拒绝策略有哪些
  		     AbortPolicy策略:直接抛出异常 线程池默认的拒绝策略;该策略会直接抛出异常,任务不会被执行
             CallerRunsPolicy策略:只用调用者所在线程来运行任务
             DiscardOldestPolicy策略:丢弃队列中即将执行的任务,并执行当前任务
             DiscardPolicy策略:不处理,直接丢弃
7.11并发辅助类
    (Semaphore、CountDownLatch、CyclicBarrier、Exchanger)
7.12 导致线程死锁的原因?怎么解除线程死锁。
      线程之间各自资源占有,等待对方释放,循环等待,造成死锁
    JDK提供了两种方式来给我们检测:
     JconsoleJDK自带的图形化界面工具,使用JDK给我们的的工具JConsole
	 jps 找到进程  然后jstack  查看进程中具体情况
     Jstack是JDK自带的命令行工具,主要用于线程Dump分析。
   定位到锁位置后
    1.缩减同步代码块范围,最好仅操作共享变量时才加锁
    2.以固定的顺序加锁
    3.采用带定时的锁
7.13 适用ThreadLocal时要注意什么?比如说内存泄漏?

8.JVM相关

8.1 JVM的内存结构
	线程私有
      1.程序计数器(唯一没有OOM的地儿)
	     作用:1.字节码解释器通过程序计数器来依次读取指令
		      2.多线程情况下,记录线程切换切换前的位置
      2.虚拟机栈(会出现栈溢出和OOM)
	     存储编译器可知对象的数据类型和对象引用
	  3.本地方法栈(会出现栈溢出和OOM)
	线程共有:
	  4.Java堆 线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象
	  5.方法区(后边被移除,变成元空间) 线程共享。存储类信息、常量、静态变量、即时编译器编译后的代码。
	  6.直接内存
	  7.运行时常量池(后边移入JAVA堆中)
8.2 什么情况下会发生栈内存溢出。
   栈溢出就是方法执行是创建的栈帧超过了栈的深度,一般方法递归就会出现这种
8.3 Eden和Survivor比例:
    Eden:Survivor0:Survivor1=8:1:1
8.4 JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。
	1.对于GC实现来说,由于它们在GC的整个工作过程中都要“stop-the-world”,如果能想办法缩短GC一次工作的时间长度就是件重要的事情。如果说收集整个GC堆耗时太长,那不如只收集其中的一部分?,而且对象大部分生命周期很短,但少量就会活很长时间,所以针对不同的特性来处理
	2.Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
8.5 对象如何晋升到老年代,说说你知道的几种主要的JVM参数。
    1.大对象直接进入老年代
    2.对象在Eden出生并经过第一次Minor GC后仍然存活,并能被Survivor容纳的话,将被移动到Survivor区,并将对象年龄设为1
     对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁。当年龄增长到一定值(默认16)后,对象被晋升到老年代。
8.6 你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。
	新生代:Serial收集器、复制算法  单线程收集器  或造成Stop-the-world
	       ParNew 收集器、Serial收集器的多线程版本,它也是一个新生代收集器。 复制算法
		   Parallel Scavenge 收集器 是一个并行的多线程新生代收集器,它也使用复制算法。 可控制吞吐量
	老年代:Serial Old收集器、 它同样是一个单线程收集器,使用“标记-整理”(Mark-Compact)算法。
	        Parallel Old收集器、 是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法
	        CMS收集器、 一种以获取最短回收停顿时间为目标的收集器   标记-清除
			     初始标记
				 并发标记
				 重新标记
				 并发清除
			优点:并发收集、低停顿,因此CMS收集器也被称为并发低停顿收集器(Concurrent Low Pause Collector)。
			缺点:1.对CPU资源非常敏感        CMS默认启动的回收线程数是(CPU数量+3)/4,CPU不足4个时(比如2个),CMS对用户程序的影响就可能变得很大
			      2.无法处理浮动垃圾(Floating Garbage)
				     由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生
				  3.标记-清除算法导致的空间碎片 
				      会造成很多空间碎片,可能导致空间很大,大对象却没地儿分配
	        G1收集器
			    初始标记
				并发标记
				最终标记
				筛选回收 
			     G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。
8.7 垃圾回收算法的实现原理。
   标记-清除 
      先对所有需要回收的对象进行标记,标记完成统一回收。
	   缺点:1.效率比较低
	        2.会造成大量空间碎片,可能导致对象因为找到连续内存,出发新一轮GC
   标记-整理 
    和标记-清除算法类似,但不清除对象,只是把对象移动到一端,然后清理掉另一端的垃圾
   复制算法 
     将内存分为两块,一块用完了,就把存活对象复制到另外一块上,然后清除这块儿垃圾
	 缺点就是,使用内存减少一半
   分代算法 
8.8 当出现了内存溢出,你怎么排错。
     内存溢出有三种情况:
	   1.有可能是内存分配确实过小,而正常业务使用了大量内存
       2.某一个对象被频繁申请,却没有释放,内存不断泄漏,导致内存耗尽
	   3.某一个资源被频繁申请,系统资源耗尽,例如:不断创建线程,不断发起网络连接
    1.直接查看日志,对堆栈信息
	2.步骤一、使用jps:jps -l
              使用ps:ps aux | grep tomat
	  步骤二、jstat”监视虚拟机各种运行状态信息。 
	         jstat -gcutil 20954 1000
	  步骤三、转存dump文件
	         jmap -histo:live 20954
			 
	3. 查发生了OOM的进程  执行top -c ,显示进程运行信息列表,键入M (大写m),进程按照内存使用排序
	   确认是不是内存本身就分配过小 jmap -heap
	   找到最耗内存的对象  jmap -histo:live 2820 | more 
8.9 怎么打出线程栈信息
   $jstack –l 23561 >> xxx.dump 
8.10 cpu 100%怎样定位
    步骤一、找到最耗CPU的进程
      执行top -c ,显示进程运行信息列表,键入P (大写p),进程按照CPU使用率排序
	步骤二、找到最耗CPU的线程
      top -Hp 线程Pid ,显示一个进程的线程运行信息列表,键入P (大写p),线程按照CPU使用率排序
	  需要使用printf 命令 将10进制转成16进制
    步骤三:查看堆栈,找到线程在干嘛
	  jstack 线程Pid
8.11 判断对象存活
    1.引用计数法 ---->无法解决重复引用问题
	2.可达性分析算法 从GCRoots出发,向下搜索,搜索所经过的路程称之为引用链,无法被引用的对象即为死亡对象
8.12 GCRoots都包含哪些
     1.虚拟机栈中引用对象
	 2.本地方法栈中的对象
	 3.方法区中静态属性引用的对象
	 4.方法区常量引用的对象
8.13 常用的JVM命令
    jps 显示所有虚拟机进程
	jstat 用于收集虚拟机各项运行数据
	jinfo 显示虚拟机配置信息
	jmap 生成虚拟机快照
	jhat 用于分析dump文件
	jstack  可以查看或导出 Java 应用程序中线程堆栈信息。
8.14 JVM几种引用
强、软、弱、虚

标题:面试篇--Java基础
作者:zc1249274251
地址:https://www.fanyueba.com/articles/2019/09/25/1569413681606.html