JAVA面试:第一章JVM


jdk的垃圾回收算法,年轻代,老年代分别是什么

  • 垃圾回收基础算法
  1. 标记清除(mark sweep) - 位置不连续 产生碎片 效率偏低(两遍扫描)

    • 第一遍扫描找到有用的,第二遍找到没用的进行清除

  2. 拷贝算法 (copying) - 没有碎片,浪费空间,移动复制对象需要调整对象引用

    • 把一块内存中有用的对象 复制到一块干净的内存中,然后把上面无用垃圾清除

  3. 标记压缩(mark compact) - 没有碎片,效率偏低(两遍扫描,指针需要调整)

    • 标记后进行压缩,移动对象指针引用需要调整

  • 以下为分代模型的垃圾回收算法

  • 年轻代:

    • Serial 串行回收
    • PS 并行回收
    • ParNew 配合CMS的并行回收
  • 老年代:

    • SerialOld 串行回收
    • ParallelOld 并行回收
    • ConcurrentMarkSweep 老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms)
      CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定
      CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收
  • 除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型

  • G1是逻辑分代,物理不分代

  • 除此之外的不仅逻辑分代,而且物理分代

  • G1(10ms)

    • 算法:三色标记 + SATB (SATB保证了在并发标记过程中新分配对象不会漏标)

  • ZGC (1ms)

    • 算法:ColoredPointers + LoadBarrier

  • Shenandoah

    • 算法:ColoredPointers + WriteBarrier

  • 1.8默认的垃圾回收:PS + ParallelOld

如何排查生产环境的OOM

  • 首先确保对OOM日志的保存

  • jinfo pid

    进程和线程的 具体信息

  • jstack

    jstack 定位线程、进程状况

  • jmap -histo pid | head -20

    查找产生对象数 排名前20 的对象具体是哪些

  • jmap -dump:live,format=b,file=heap.bin pid

    生成dump转储文件,线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)

  • 使用MAT / jhat /jvisualvm 进行dump文件分析
    https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html

  • jhat -J-mx512M xxx.dump

分析日志很重要

dump 文件里,值得关注的线程状态有:

  • 死锁,Deadlock(重点关注)

  • 执行中,Runnable

  • 等待资源,Waiting on condition(重点关注)

  • 等待获取监视器,Waiting on monitor entry(重点关注)

  • 暂停,Suspended

  • 对象等待中,Object.wait() 或 TIMED_WAITING

  • 阻塞,Blocked(重点关注)

  • 停止,Parked

    tid指Java Thread id。nid指native线程的id。prio是线程优先级。

    [0x00007f1d2c4be000] 是线程栈起始地址。

    Thread.State:WAITING (parking) 等待,线程挂起中。

    parking to wait for <0x00000000f5dee038>

    本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue 并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。

dump文件实例分析

https://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html

jconsole远程连接

https://blog.csdn.net/wangnanwlw/article/details/102381882

  1. 程序启动加入参数:

    java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar test.jar 
    
  2. 如果遭遇 Local host name unknown:错误,修改/etc/hosts文件,把ip加入进去

    192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
    
  3. 关闭linux防火墙(实战中应该打开对应端口)

    service iptables stop
    chkconfig iptables off #永久关闭
    
  4. windows上打开 jconsole远程连接 192.168.17.11:11111

jvisualvm远程连接

https://www.cnblogs.com/liugh/p/7620336.html (简单做法)

jprofiler (收费)

arthas在线排查工具

  • 为什么需要在线排查?
    在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因。为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布,如果打了日志还是没找到问题,继续加日志,重新打包发布。对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度。
  • jvm观察jvm信息
  • thread定位线程问题
  • dashboard 观察系统情况
  • heapdump + jhat分析
  • jad反编译
    动态代理生成类的问题定位
    第三方的类(观察代码)
    版本问题(确定自己最新提交的版本是不是被使用)
  • redefine 热替换
    目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性
    m() -> mm()
  • sc - search class
  • watch - watch method
  • 没有包含的功能:jmap

jvm的内存模型

  • Java Memory Model

  • 一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

  • jmm是jvm的一种规范,定义了jvm的内存模型。它屏蔽了各种硬件和操作系统的访问差异,不像c那样直接访问硬件内存,相对安全很多,它的主要目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。可以保证并发编程场景中的原子性、可见性和有序性。
    链接:https://www.jianshu.com/p/76959115d486

  • 根据JVM虚拟机规范,管理内存如下

PC 程序计数器 (独享)

存放指令位置

虚拟机的运行,类似于这样的循环:

while( not end ) {

​ 取PC中的位置,找到对应位置的指令;

​ 执行该指令;

​ PC ++;

}

JVM Stack 、Frame (独享)

  • 栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接

    (Dynamic Linking)、方法返回值和异常分派

  • 栈帧随着方法调用而创建,随着方法结束而销毁

  • 栈帧的存储空间分配在 Java 虚拟机栈之中,每一个栈帧都有自己的局部变量表(Local Variables)、操作数栈(OperandStack)和指向当前方法所属的类的运行时常量池的引用。

  1. Frame - 每个方法对应一个栈帧
    1. Local Variable Table
    2. Operand Stack
      对于long的处理(store and load),多数虚拟机的实现都是原子的
      jls 17.7,没必要加volatile
    3. Dynamic Linking
      https://blog.csdn.net/qq_41813060/article/details/88379473
      jvms 2.6.3
    4. return address
      a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方

Heap (共享)

  • 在 Java 虚拟机中,堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例
    和数组对象分配内存的区域。
  • Java 堆在虚拟机启动的时候就被创建,它存储了被自动内存管理系统,也即是常说的“Garbage Collector(垃圾收集器)”)所管理的各种
    对象,这些受管理的对象无需,也无法显式地被销毁。

Method Area (共享)

  1. Perm Space (<1.8)
    字符串常量位于PermSpace永久区
    FGC不会清理
    大小启动的时候指定,不能变

  2. Meta Space (>=1.8)
    字符串常量位于堆
    会触发FGC清理
    不设定的话,最大就是物理内存

    它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法

Runtime Constant Pool

  • 每一个运行时常量池都分配在 Java 虚拟机的方法区之中
  • 在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。

Native Method Stack

  • JVM实现使用到传统的栈(通常称之为“C Stacks”)来支持 native 方法(指使用 Java 以外的其他语言编写的方法)的执行,这个栈就是本地方法栈,一般会在线程创建的时候按线程分配。
  • 当 Java 虚拟机使用其他语言(例如 C 语言)来实现指令集解释器时,也会使用到本地方法栈。

Direct Memory

JVM可以直接访问的内核空间的内存 (OS 管理的内存)

NIO , 提高效率,实现zero copy


java如何加载类的,哪种情况下不需要用java的双亲委派模型来加载类(java的类加载原理)

双亲委派的打破

  1. 如何打破:重写loadClass()
  2. 何时打破过?
    1. JDK1.2之前,自定义ClassLoader都必须重写loadClass()
    2. ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
    3. 热启动,热部署
      1. osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)

hashMap的原理,1.7和1.8的区别是什么,如何保证并发安全

  • JDK7 数组+链表
  • JDK8 数组+链表+红黑树 链表长度大于等于8 红黑树 小于等于6 链表
  • new HashMap() 时初始容量是0
  • put()的时候 开辟空间

HashMap在jdk1.8之后引入了红黑树的概念,表示若桶中链表元素超过8时,会自动转化成红黑树;

若桶中元素小于等于6时,树结构还原成链表形式。

原因:

  • 链表的时间复杂度是O(n),红黑树的时间复杂度O(logn),很显然,红黑树的复杂度是优于链表的
  • 红黑树的平均查找长度是log(n),长度为8,查找长度为log(8)=3,
  • 链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,
  • 其实在大于5时,红黑树已经优于链表的 平均查找长度
  • 树节点所占空间是普通节点的两倍,所以只有当节点足够多的时候,才会使用树节点。
  • 也就是说,节点少的时候,尽管时间复杂度上,红黑树比链表好一点,但是红黑树所占空间比较大,综合考虑,认为只能在节点太多的时候,红黑树占空间大这一劣势不太明显的时候,才会舍弃链表,使用红黑树。

选择6和8的原因是:

  • 中间有个差值7可以防止链表和树之间频繁的转换。
  • 假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。

如何保证并发安全

  • 多线程下使用HashMap会出现了死循环和丢失数据,导致部分线程一直运行,占用cpu时间。
    • 问题原因就是HashMap是非线程安全的,多个线程put的时候造成了某个key值Entry key List的死循环,问题就这么产生了。
      当另外一个线程get 这个Entry List 死循环的key的时候,这个get也会一直执行。最后结果是越来越多的线程死循环,最后导致服务器宕机
  • 方法上加Synchronized或者方法内部map执行操作时进行加锁
  • 强烈建议使用concurrentHashMap解决多线程并发问题

concurrentHashMap的并发原理,1.7和1.8的区别是什么

https://www.jianshu.com/p/e694f1e868ec

原文地址:https://www.cnblogs.com/JMrLi/p/13918032.html