jdk的垃圾回收算法,年轻代,老年代分别是什么
- 垃圾回收基础算法
-
标记清除(mark sweep) - 位置不连续 产生碎片 效率偏低(两遍扫描)
-
第一遍扫描找到有用的,第二遍找到没用的进行清除
-
-
拷贝算法 (copying) - 没有碎片,浪费空间,移动复制对象需要调整对象引用
-
把一块内存中有用的对象 复制到一块干净的内存中,然后把上面无用垃圾清除
-
-
标记压缩(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
-
程序启动加入参数:
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
-
如果遭遇 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
-
关闭linux防火墙(实战中应该打开对应端口)
service iptables stop chkconfig iptables off #永久关闭
-
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)和指向当前方法所属的类的运行时常量池的引用。
- Frame - 每个方法对应一个栈帧
- Local Variable Table
- Operand Stack
对于long的处理(store and load),多数虚拟机的实现都是原子的
jls 17.7,没必要加volatile - Dynamic Linking
https://blog.csdn.net/qq_41813060/article/details/88379473
jvms 2.6.3 - return address
a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方
Heap (共享)
- 在 Java 虚拟机中,堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例
和数组对象分配内存的区域。- Java 堆在虚拟机启动的时候就被创建,它存储了被自动内存管理系统,也即是常说的“Garbage Collector(垃圾收集器)”)所管理的各种
对象,这些受管理的对象无需,也无法显式地被销毁。
Method Area (共享)
-
Perm Space (<1.8)
字符串常量位于PermSpace永久区
FGC不会清理
大小启动的时候指定,不能变 -
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的类加载原理)
双亲委派的打破
- 如何打破:重写loadClass()
- 何时打破过?
- JDK1.2之前,自定义ClassLoader都必须重写loadClass()
- ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
- 热启动,热部署
- 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也会一直执行。最后结果是越来越多的线程死循环,最后导致服务器宕机
- 问题原因就是HashMap是非线程安全的,多个线程put的时候造成了某个key值Entry key List的死循环,问题就这么产生了。
- 方法上加Synchronized或者方法内部map执行操作时进行加锁
- 强烈建议使用concurrentHashMap解决多线程并发问题