jvm汇总

(1)JAVA运行时类什么时候被加载

 用到的时候才会加载 没有用到不会加载, 按需加载

(2)jvm一个类的加载过程

1加载 .clas文件 jar包 网络 磁盘目录下的类.class以二进制字节流读进来,在内存中生成一个代表这个类的java lang class 对象放入元空间,此时可以干预,可以自定义类加载器来实现类的加载

    2 (2.1验证2.2准备2.3解析)链接

  验证 就是验证class文件字节流是否合法 符合规范

准备 类变量赋默认值初始化 如int为0

         常量是在准备阶段就会直接赋值

解析 把符合引用翻译为直接引用(把变量类型转化为内存地址感觉)

3初始化 new一个类对象 访问类的静态属性 反射api 对类的调用

4使用 调用这个类

5卸载 该类全部实例都被gc 该类的java lang class 对象没有任何引用情况下

(3)一个类被初始化过程

  类的初始化阶段 JAVA虚拟机才真正开始执行类中编写的JAVA程序代码

 进行准备阶段 变量已经赋过一次系统要求的初始零值,而在初始化阶段,才真正初始化类变量和其他资源 常量在准备阶段赋真正值, 静态变量在准备阶段赋默认值 在初始化阶段赋真正值,变量在new对象才会赋值 静态代码块在初始化阶段就会执行 ,普通代码块在创建类对象被执行,构造器在创建对象才会被执行 

 (4)继承父子类初始化顺序

    父类静态变量 父类静态初始化代码块 子类静态变量 子类静态初始化块 父类变量 父类初始化块 父类构造器 子类变量 子类初始化块 子类构造器 

(5)什么是类加载器

    在类的加载阶段 通过一个类的全限定名来获取描述该类的二进制字节流的这个动作的代码被称为 类加载器 可以自定义

(6)jvm有那些类加载器

       在JAVA虚拟机角度只有两种

          启动类加载器 c++实现 虚拟机一部分

          其他所有类加载器 由JAVA语言实现 独立在虚拟机外部 并且全都继承抽象类 java lang classloader

      在java开发者角度有三层类加载器

           bootstrapclassloader extclassloader appclassloader 自定义类加载器

(7)jvm不同类加载器加载那些文件

      启动类加载器 jre/lib/ rt.jar

      扩展类加载器 jre/lib/ext

      应用类加载器 appeclassloader(就是自己写的JAVA代码)

(8)jvm三层类加载器之间的关系是继承关系吗

       不是继承关系,自定义类加载器继承classloader

(9)了解jvm类加载的双亲委派模型吗

        就是首先加载自己不去加载 委派给上一层的父类加载 找到最顶层 最顶层开始加载 最顶层没有找到再向下加载

 首先自底向上 appclassloader -extclassloader-bootstrapclassloader 

然后自顶向下加载

让bootstrapclassloader 类去加载 没有找到 让extclassloader加载 没有找到让appclassloader自定义类去加载

  都没有找到 会报class not find

 

(10)jdk为什么要设计双亲委派模型

    确保安全 避免JAVA核心类库被修改

    避免重复加载 

    保证类和包名的唯一性

  比如 自己写一个string类 ,不会被加载自己写的 ,加载的是跟的rt.jar包下的string

(11)可以打破双亲委派模型吗

  可以 自定义一个类继承classloader 重新loaderclass方法,

(一个类请求过来 ,会调用appclassloader里面的loaderclass方法,这个方法作用是查找加载类不在当前范围内 ,查找本地缓存之前有没有加载过,没有加载过会包异常,找到的话会找父类)

(双亲委派源码 查找当前类有没有加载过,在元空间中,加载过 直接返回 没有加载过 判断上一层父类不等null 就是ext 找ext的loaderclass方法 知道找到该方法,最后又会调用到loaderclass 类似递归 上一层等于空,会调用findbootstrapclassornotname启动类加载器找 这就是刚开始自下而上查找, 如果bootstrapclassloader没有找到 反过来自顶向下查找)

(12)如何自定义自己的类加载器

  继承classloader

  覆盖 findclass(string name)方法 不会打破双亲委派

或者loadclass()方法 可以打破双亲委派 因为loadclass方法里面的代码是双亲委派核心代码

(13)classloader中loadclass findclass defineclass区别

  loadclass 主要进行类加载的方法 默认双亲委派机制就实现在这个方法

findclass 根据名称或位置加载class字节码

defindclass 把字节码转化为java lang

class

(14)加载一个类采用classforname 和classloader有什么区别

   class.forname得到class是已经初始化完成的

  classloader.loaderclass得到的class是还没有初始化的

  classforname加载类的时候会初始化类的信息 加载 链接 初始化

  classloader加载类的时候不会对类初始化 只会加载(验证准备解析)链接 new对象的时候才会初始化

(15)java代码怎么运行的  

  JAVAc编译class文件 java class文件 在jvm上运行

(16)jvm整个运行流程

  1首先java的源文件 通过javac编译成字节码文件,通过类加载子系统(classloader sub system)加载到jvm内存中,

2加载的字节码文件放入元空间(方法区或者永久代)

3main方法执行,创建一个虚拟机栈 也是线程栈

4运行的时候有局部变量 放入虚拟机栈中 如果引用的对象放到堆里面,同时代码执行的时候有个程序计数器,记录代码执行到第几行

5如果方法中有需要调用本地方法 也就是native方法 会创建本地方法栈

6在代码执行的过程中有的对象用过不再使用了 这是这个对象变成垃圾了 ,这个由垃圾回收器回收回收堆里面垃圾对象

(17)介绍一下jvm内存结构划分

线程私有

虚拟机栈 存储方法 局部变量 运行数据

本地方法栈 存储native方法

程序计数器 字节码行号 

线程共享

堆 存储 创建对象 数组

元空间 存储 加载一个类会把这个类放到元空间 静态变量 常量 运行时常量池

(18)jvm运行时数据区 程序计数器特点和作用

  当前线程执行的字节码行号指示器

  java多线程执行时 每个线程都有一个独立的程序计数器 

 该区域线程私有 每个线程独立存储

没有gc回收

 

       (19)jvm运行时数据区 虚拟机栈特点和作用

     线程私有 方法执行创建栈帧(局部变量 操作数栈 动态链接 返回地址)存储局部变量表

  先进后出 栈异常 stackoverflowerror(递归调用死循环创建方法)

  栈里面运行方法 存放方法的局部变量 变量名所指向的值 (常量值 对象值)都存在堆上

加载一个类加载到元空间 对象放到堆里面

 -Xss1M设置栈大小

 随着线程创建而创建 线程结束而结束栈

  (20)jvm运行时数据区 本地方法栈特点

       本地native方法服务的 随线程而生而灭 

      gc不会回收该区域 和虚拟机栈特点一样

(21)jvm运行时数据区 JAVA堆特点和作用

     线程共享 虚拟机启动创建 虚拟机内存最大的一块区域 存放所有的实例对象或数组

  gc垃圾收集器主要管理区域 分为新生代和老年代 新生代细分为eden from区 to区 占比为 8:1:1 可以通过 -Xmx -Xms 调正堆大小 如果从分配内存来看所有线程共享JAVA堆中可以划分多个线程私有分配缓冲区 (tlab)提升对象分配时效率,多线程往堆里面放数据会有竞争 提升效率给每个线程分配一个默认的区域 当默认区域满了之后往堆公共区域放数据 这时有竞争 加锁

(22)jvm中对象如何在堆内存分配

     指针碰撞 内存规整情况下

     空闲列表 内存不规整情况

     选择那种看jvm堆内存是否规整,JAVA堆是否规整又是由采用的垃圾收集器是否带有空间压缩整理决定的  

当使用serial parnew等带压缩整理过程收集器时,系统采用指针碰撞  

当使用cms这种基于清除算法收集器 理论上采用较为复杂的空闲列表分配内存

    本地线程分配缓冲(tlab)解决线程竞争以空间换时间方式 默认开启的

(23)jvm堆内存中对象布局

 在hotspot虚拟机中一个对象的存储结构分为3块区域 对象头 实例数据 对齐填充

  对象头 包含两部分 第一部分存储对象自身运行数据,如 哈希码 gc分代年龄 锁状态标志 第二部分 类型指针 对象指向它的类元数据指针

实例数据 程序代码中定义的成员变量类型的字段内容

 对齐填充 不是必然需要 主要占位 

(24)jvm什么时候发生堆内存溢出

  JAVA堆中用来存储对象 只要不断创建对象 并且gcroot到对象之间有可达路径 当总容量到达最大堆容量限制 会产生内存溢出

   MAT工具分析 xxx.hprof快照文件 排查溢出问题 

 

   (25)jvm怎么判断对象可以被回收

        通过可达性分析算法来判断对象是否存活 gcroot根对象没有任何引用的对象可以被回收

    局部变量引用的对象可以作为gcroot

   A a=new A newA在堆 栈中a局部变量可以作为gcroot

 被syn持有的对象

(26)java不同的引用类型

  强引用 Object obj=new Object

  软引用 SoftReference gc时内存充足不回收,不足回收 用于缓存

   弱引用 WeakReference gc时不管充足不充足都会回收

  虚引用 PhantomReference

   (27)jvm堆内存分代模型

     分为新生代 (占整个内存3/1) 老年代(占3/2)

   新生代 分为 eden from区 to区 占比 8:1:1

   (28)jvm堆中新生代垃圾回收过程

   new一个对象在eden区分配 随着对象的创建 eden会创建满 再创建对象放不进去 会触发minongc,有些对象处在存活状态,把存活的对象移动到from,当再次gc时 会把eden和from进行垃圾回收 存活对象放到to区

如果一个对象一直存活了一个阈值(15次)

将对象放入老年代

(30) jvm对象动态年龄判断是怎么回事

  survivor区对象年龄从小到大相加 累加到一定年龄占用空间大于50%(可以用-XX.TargetSurvivoRatio=)设置保留多少空间 默认值50 比一定年龄大的对象晋升老年代

(31)什么是老年代空间分配担保机制

   理解为 对象首先会放入新生代 。一直存活对象到一定阈值年龄就是进入老年带去,还有大对象新生代放不下也会直接放入老年代 当新生代和老年代都内存不足的时候 触发fullgc 当只有老年代内存不足会先尝试触发minongc回收新生代

 

(32)什么对象进入老年代

  超过一定gc阈值 动态判断年龄 大对象直接进入老年代 老年代空间担保机制

(33)jvm运行时数据区元空间特点作用

   jdk1.8才叫元空间 之前叫方法区/永久代

 元空间线程共享的 

存储 被加载类信息 常量 静态变量 常量池  

 元空间采用本地内存 -XX MetaspaceSize设置大小

元空间很少gc 元空间内存不足 oom

(34)jvm本机直接内存

            直接内存不属于jvm运行时数据区 是本机直接物理内存 

(35)jvm内存相关的核心参数

  -Xms java堆内存大小

 -Xmx java堆内存最大大小

  -Xmn java堆内存新生代大小

  -XX MeatspaceSize元空间大小

-Xss 每个线程栈内存大小

 -XX SurvivoRatio=8 设置eden和Survivor区大小比例 默认8:1:1

-XX:MaxTenuringThreshold=5 年龄阈值

-XX:+UseG1GC指定使用g1垃圾回收器

(36)怎么计算一个对象大小(了解)

    iucene工具包

(37)堆为什么要分新生代和老年代

  新生代特点 创建对象很快回收 需要一种垃圾算法

 老年代对象 需要长期存活 需要另一种垃圾回收算法

jvm划分新生老年代之后 垃圾收集器每次只回收其中某个区域也就有了 Minorgc majorgc fullgc

  minorgc 新生代收集 收集

  majorgc 老年代回收 

fullgc 整堆收集 java堆和元空间垃圾回收器

  Mixedgc 混合收集 整个新生代和部分老年代 目前只有g1收集器会有这种行为

   针对不同区域采用不同垃圾收集算法

   标记复制 标记清除 标记整理

(38)堆新生代为什么要有from to区

   为了保证有一快区域是空白的 复制存活对象效率高 减少老年代fullgc

(39)jvm中的垃圾回收算法

  复制算法 标记清除 标记整理 分代收集算法

   标记清除 标记所有需要回收的对象 在标记完成后 统一回收所有被标记的对象 标记过程就是对象是否属于垃圾的判断过程 用可达性分析算法判断对象是否可以回收,标记后对所有对象进行回收

   优点 基于最基础的可达性分析 实现简单 后续收集器基于这种思想实现的

  缺点 执行效率不稳定 如果java堆包含大量对象 需要回收这时需要大量标记 导致标记和清除过程执行效率随对象数量增长而降低

     内存空间碎片化问题 标记 清除之后产生大量不连续的内存碎片 可能会导致当以后需要分配较大对象无法找到足够内存不得不提前触发另一次垃圾收集器

  标记复制算法

        将可用内存安容量分大小相等两块 每次只使用其中一块 当内存用完 将存活对象复制到另一快内存 把使用过的内存空间一次清理 复制对象的时候是连续的存放

    优点 实现简单 效率高 解决了标记清除算法导致的内存碎片

   缺点 代价大 将可用内存缩小一半 空间浪费 在对象存活很多的时候复制的时候效率比较低 所以复制算法不合适老年代 复制算法一般回收新生代内存空间

 

标记整理算法

   标记整理适用于老年代  

   标记过程是可达性分析算法进行标记

   和标记清理不同的是 不是针对可回收的对象进行清理 而是根据存活对象进行整理 让存活对象都向一端移动 然后直接清理掉边界以外内存 

   标记清除算法不移动存活对象 导致大量不连续空间 内存碎片 老年代这种每次回收都有大量存活对象区域 移动存活对象并更新所有引用这些对象的引用 比较耗时 移动 操作必须暂停用户线程才能进行 这种暂停stw

  分代收集算法

    现在一般虚拟机垃圾收集采用分代收集算法

    根据对象存活周期不同将内存分为几块 一般把java堆分为新生代和老年代 jvm根据各个年代特点采用不同收集算法

     新生代 每次进行垃圾回收都会有大量对象死去 采用复制算法 

    老年代 因为对象存活率高 采用标记清理 加标记整理算法进行回收

(40)介绍一下jvm垃圾收集器

  新生代 Serial parnew ParallelScavenge

  老年代 cms serialOld parallelold

   整堆收集器 g1 可以收集新生代和老年代

   垃圾回收器最新的是zgc在gdk11 和shenandoah实验阶段

  

 serial(英文 涩瑞儿)收集器

    新生代收集器 单线程 串行 收集暂停用户线程工作 stw

    开启jvm参数-XX:+UseSerialGC=Serial(新生代 复制算法 ) +SerialOld(老年代 标记整理算法)

    回收过程 多核cup线程同时运行 这时内存不足 会触发垃圾回收 会在一个安全点停下来 停下来原因因为在垃圾回收过程中对象的地址可能发生改变 为了安全使用对象 需要把用户线程暂停 进行垃圾回收 只有一个垃圾线程在处理 其他暂停 serial是单线程

 

ParNew收集器

   和serial一大部分一样 不同点是在进行垃圾回收的时候会开启多个线程进行垃圾回收(开几个线程看cpu是几核的)

 serial和parnew收集器可以配合cms收集器 

  前者收集新生代 后者收集老年代  

-XX+UseConcMarkSweepGC 开启cms

开启cms之后默认会使用parnew作为新生代垃圾收集器

  收集器 -XX:+UseParNewGC强制指定使用parnew

     -XX:+ParallelGCThreads=2指定垃圾收集的线程数量 parnew默认开启收集器线程和cpu数量一样

 

 Parallel Scavenge(英文 怕我蓝儿 四凯门治)收集器

   简称 parallel 新生代收集器 基于复制算法 并行的多线程垃圾收集器 侧重于达到一个可控的吞吐量 虚拟机运行100分钟 垃圾收集花1分钟 则吞吐量为99% 有时间我们把该垃圾收集器叫吞吐量垃圾收集器 而且这个垃圾收集器是jvm默认的垃圾收集器

  -XX:+UseAdaltiveSizePollcy自适应新生代大小策略 默认这个参数开启 不需要人工指定新生代大小(-Xmn)只需要把基本的内存数据设置好(如-Xmx设置最大堆) 然后使用-XX:MaxGCPauseMillis参数(最大停顿时间)就好了

   

  serialold收集器 

      单线程 可在client模式使用 也可以在service模式使用 采用标记整理 serialold收集器可以做为cms收集器失败时的后备预案

一般不会用

  parallelold收集器

     parallelold 多线程 标记整理 jdk1.6开始提供

 cms收集器

     老年代垃圾收集器 是最短回收停顿时间为目标的收集器 互联网b/s结构 服务端适合用次收集器

  垃圾回收会带来stw问题 会导致系统卡死时间长 cms 垃圾回收采取垃圾回收线程和系统工作线程同时执行来处理的 标记清除算

老年代是cms 新生代默认是parnew

 

      cms垃圾收集器运作过程 4个阶段

  1初始标记 (会stw 标记一下gcroot 能直接关联的对象 这些对象是存活的对象 速度很快)

2并发标记(不会stw 追踪gcroot的整个链路 从gcroot直接关联对象遍历整个对象引用链路 这个过程较长 但是不需要停顿用户线程 可以和垃圾回收线程一起并发执行)

3重新标记(会stw 修正并发标记期间 因用户线程继续运行尔导致标记产生变化的那一部分对象标记记录 这个阶段的停顿时间比初始标记长一点 但也远比并发标记阶段时间短 它其实就是对第二阶段中被系统程序运行变动过的少数对象进行标记 所以运行快)

4并发清理 (不会stw 清理删除掉标记阶段判断已经死亡的对象 这个阶段很耗时 但是由于不需要移动存活对象 这个阶段也是和用户线程并发执行 因为cms是标记清除 +标记整理 首先是清除 当内存碎片到达一定的限制 会进行整理)

  缺点  

   并发收集会占用cpu 会产生浮动垃圾 这些垃圾没有清除 

cms标记清除算法 会产生碎片空间 在进行fullgc垃圾回收的时候会进行碎片内存整理

 

G1收集器

  收取新生代和老年代 cms适合于1.9和1.8 以上不能用 是警告 1.11版本用的是g1

 

  g1可以设置垃圾回收预期停顿时间 参数 -XX:MaxGCPauseMIllis 默认200ms

  对内存合理分配 优化jvm参数 就是为了减少新生代(minor gc) 或者整个老年代(majorgc) 或者整个堆(fullgc)

g1垃圾收集器是尽量把垃圾回收对系统造成影响控制在设置的指定时间范围内 同时在有限的时间尽量回收多的垃圾对象 这是g1的核心原理

 g1怎么做到可预测停顿时间

   这和g1垃圾收集设计有关 把java整个堆内存拆分多个大小相等的region(英文 瑞智)分区 (eden survivor old humongous大对象)

  会追踪每个region的回收价值 计算每个region里面对象有多少垃圾 如果对这个region回收 需要多少时间 可以回收多少垃圾

比如g1通过追踪发现 一个region垃圾对象10mb 回收需要500ms 另一个region垃圾对象20mb 回收时间需要100ms 那只会回收耗时段 并且回收垃圾多的

 

g1对新生代的回收

 g1新生代也有eden和survivor 随着不停在新生代eden对应的region中放对象 jvm就会不停给新生代加入更多的region 直到新生代占据堆大小最大比例60%

 g1采用复制算法 进行新生代垃圾回收 stw 把eden对应的region中存活对象复制到s0对应的region 

  g1认为只要超过一个region容量一半的对象即可判定为大对象 

 

老年代回收过程

  初始标记 需要stw 并发标记 不需要stw

 重新标记 需要stw 筛选回收 需要stw

 

内存泄露和内存溢出

  溢出 oom 没有足够的空间

  泄露 mermory leak 程序运行后没有释放所占用的空间 ,

线上环境jvm设置多大

  线上 4核8G

    jvm 栈 堆 元空间

    1 栈1m xss512k 一个线程是1m 

       堆 大概把机器一半内存给堆 4G

     元空间 一般512M 不设置用的机器物理内存

   堆 -Xms4096 栈 Xss1M    

元空间Metaspacesize512M

  

线程服务内存飙升

   top命令 查看进程   

  查看进程id jps -l

 先把服务从负载均衡把服务踢掉 

  jmap -histo pid 分析哪个文件占用大

 jmap -heap pid 查看jvm内存占用

jmap -dump:format=b.file=heap.hprof pid

堆文件导出 (线上禁用 很大内存占用)

用eclisp下的工具MAT分析 heap.hprof

 

  线上项目cpu飙升

       先用top命令看下那个进程占用cpu高 pid 

     top -H -p pid 查看pid进程下的线程 看哪个线程占用多 他是十六进制的

     printf `%x' pid 将pid转化成十六进制

      jstack pid gdk命令 它的线程信息是十六进制的 

  

  jvm堆溢出后 其他线程是否可以继续工作

     可以访问 用vm工具 jdk的    

堆溢出后 要是溢出的代码是局部变量

 内存释放 其他线程可以访问    

 

要是溢出的代码是成员变量 访问不了 成员变量没有被释放内存 

 

项目oom了怎么办

   

 

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值