JAVA内存模型及jvm调优详解

本文详细阐述了JVM的类加载机制、类的加载时机、初始化过程、双亲委派模型及其优势,以及内存结构(包括栈、堆、元空间)、垃圾回收算法和内存溢出处理。此外,还介绍了自定义类加载器和JVM性能调优的相关参数。
摘要由CSDN通过智能技术生成

jvm 基础

1.jvm 类加载机制

Java采用的事Hotspot 虚拟机,按需加载 ,懒加载的方式。

2.类的加载时机

        1.使用new实例化对象时,读取或设置一个类的静态字段或方法时;

        2.反射调用时,如Class.forName("com.xx.MyTest");

        3.初始化一个类的子类,会首先初始化子类的父类;

        4.jvm启动时标明的启动类

        5.jdk8,接口存在default方法,接口的实现类初始化时,接口会先初始化。


3.类的加载过程

        .java --->通过javac命令--->.class,classLoader 将.class文件中的二进制数据读入到内存中,

并将其放在元空间,然后在堆区创建一个java.lang.Class对象,用来封装元空间的数据结构,类加

载的最终产物是Class对象,他封装了类在方法区的数据结构,并向程序员提供了访问方法区数据

结构的接口。

4.类的初始化的过程:

        1.类变量赋值(真正的值),执行静态代码块;普通代码块在实例化时执行;

        2.实例化时,静态代码,变量,普通代码块,构造器。

        3.父子类的初始化关系

                父静态变量/代码块->子类静态变量/静态代码块->父类变量/代码块/构造器 -> 子类变量/

代码块/构造器


5.类的生命周期

加载->验证->准备->解析->初始化->使用->卸载

  • 加载:读取二进制字节码,静态存储结构转化为方法区的数据结构,在堆中生成java.lang.Class对象
  • 验证:文件格式验证、元数据验证、字节码验证、符号引用验证
  • 准备:静态变量(类变量、static)分配内存,初始化默认值(0\null\false)。同时被final和static修饰,初始化值。
  • 解析:符号引用(类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用限定符)字面量 转变为直接引用(指针、相对偏移量、间接定位到目标的句柄)。
  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块
  • 使用:
  • 卸载:所有的实例已销毁、该类的ClassLoader已被GC,类对应的java.lang.class没有被引用

6.类加载器

  • 启动类加载器:Bootstrap ClassLoader----> /jre/lib
  • 扩展类加载器:Extention ClassLoader---->/jre/lib/ext
  • 应用程序类加载器:AppClassLoader(系统类加载器)---->classpath
  • 自定义加载器:负责加载用户自定义路径下的类名,继承java.lang.ClassLoader

7.双亲委派:

        双亲委派机制是指如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个

类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到

顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加

载,子加载器才会尝试自己去加载该类。


7.1双亲委派机制好处:

  1. 沙箱安全机制:保护java自己的类库,如String
  2. 避免类的重复加载(相同包名类名只加载一份)findLoadClass()
  3. 保证类的唯一性

  7.2 如何打破双亲委派模型?


                自定义一个类加载器,重写其中loadClass方法,使其不适用双亲委派模型


7.3 如何自定义类加载器

  •  继承ClassLoad类,覆盖findClass(String name)(不打破双亲委派) 或者 loadClass()
  •  defineClass()----->C++实现,把字节码转化为java.lang.Class
  •  class.forName()(会初始化)和loadClass()(不会初始化)

 7.4jvm 参数:
          

  -XX:TraceClassLoding

java 内存模型

1.虚拟机栈

  • 存放的数据包括:基本数据类型、对象的引用、方法帧、运行数据、指令、返回地址。(先进后出)
  • 局部变量表:方法参数和局部变量
  • 操作数栈:临时存放执行方法时要操作的数据
  • 动态链表:
  • 方法出口:
  • jvm 参数:-Xss:每个线程栈内存大小

2.本地方法栈:native方法

3.程序计数器:

        记录字节码的行号,也就是下一条指令


4.虚拟机堆:

        对象、数组,静态变量(jdk1.8)gc操作对象,(新生代(1/3,Eden 8/10,from 1/10,to 1/10)、老年代)tlab
         jvm参数:

  •                 -Xms:初始化堆大小
  •                 -Xmx:堆的最大值
  •                 -Xmn:新生代大小
  •                 -XX:NewSize 新生代的初始值
  •                 -XX:MaxNewSize 新生代的最大值

5.元空间(方法区):

        类信息(类全称、父类全程),域信息(域名称、域修饰服等),方法信息(名称、修饰符、返回类型等),运行时常量池,final,(持久代)
         jvm参数:

  •                 -XX:MetaspaceSize;
  •                 -XX:MaxMetaspaceSize
  1. 1.Java代码是如何运行起来的?.java->javac class  java java -jar  tomcat startup.sh->catalina.sh-> Bootstrap中的main
  2. jvm运行原理图,内存结构图?
  3. 请介绍一下JVM的内存结构的划分?
  4. JVM哪些区域事线程私有(线程安全)的?哪些区域是线程共有的?

   jvm 垃圾回收机制


 1.jvm 如何判断对象是否可以回收?


              引用计数法(老)
              可达性分析算法,gc roots 引用链,(栈中的引用的对象、元空间区静态变量、元空间常

量、jni、基本数据类型对应的class、常驻异常、synchronization对象)


2.java的引用类型?

  •  强引用:new 内存溢出也不回收
  • 软引用:SoftReference 内存充足不回收,内存不足回收。适合做缓存,mybatis框架
  • 弱引用:WeakReference gc运行回收 jdk中有ThreadLocal
  • 虚引用:PhantomReference gc回收时发送系统通知


  3.jvm垃圾回收算法?

  •  标记-清除法 效率不高,内存碎片
  • 标记-复制算法 内存利用率低
  • 标记-压缩(整理)算法 性能低
  • 分代收集算法:新生代采用复制算法,老年代采用标记-压缩算法  

         
4.jvm堆中新生代的垃圾回收过程?

  •  Eden满了->Minor GC -> from Surviver,清空Eden --> Eden满了 ---> Minor GC -> to Surviver,清空Eden、from Surviver  ..... -> gc 年龄=15  ->  老年代
  • 15次 -XX:MaxTenuringThreshold=10
  • CMS gc 6次
  • gc 默认为 并行gc
  • jvm参数

     查看当前gc  java -XX:+PrintFlagsFinal
                            java -XX:+PrintCommandLineFlags


5.JVM动态年龄判断是怎么回事?

Survivor区的对象年龄从小到大进行累加,当累加的x年龄时综合大于50%,那么比x大的都会晋升到老年代50% 可以通过jvm参数: -XX:TargetSurvivorRatio=? 设置


            6.JVM 空间分配担保机制?


                Minor GC ---->检查老年代可用空间是否大于新生代对象总大小?大于Minor gc 小于----> 老年代可用空间大小,是否大于历史对象均值?小于 Full gc 大于---->Minor gc 对象还是放不下 Full GC
                目的:减少Full GC的次数


            7.什么情况下对象进入老年代?


                1)gc年龄大于15 ,cms gc 默认为6
                2)动态年龄判断
                3)jvm老年代空间分配担保机制。
                4)大对象直接进入老年代,通过参数 -XX:PretenureSizeThreshold 设置


            8.JVM垃圾收集器


                新生代收集:Minor GC / Yong GC
                老年代收集:Major GC /Old GC
                整堆收集: Full GC
                混合收集: Mixed GC
                
                Minor GC 
                    Serial :标记-复制算法,串行,暂停用户工作 -XX:+UseSerialGC
                    ParNew :是Serial的多线程版本   -XX:+UseParNewGC -XX:+ParallelGCThreads=2 
                    Parallel Scavenge:复制算法,并行多线程,侧重吞吐量,jvm默认  -XX:MaxGCPauseMillis=50 毫秒,暂停时间 -XX:+UseAdaptiveSizePolicy 自适应新生代大小策略
                    
                Major GC 
                    SerialOld : 标记-整理算法
                    ParallelOld:jdk1.8 默认
                    CMS ConcurrentMarkSweepGC  最少停顿时间  标记清除算法,多线程,和应用程序并行 -XX:+UseConcMarkSweepGC 此时新生代 使用的ParNewGC

jvm 性能调优

1.原则


            减少Full GC的频率,或者避免FullGC,FullGC耗时 
            

jvm常用参数:

  • -Xms2048m:初始堆大小,建议<物理内存的1/4,默认值为物理内存的1/64
  • -Xmx2048m:最大堆大小,建议与-Xms保持一致(因为如果不一致,会扩容,降低性能),默认值为物理内存的1/4
  • -Xmn512m:新生代大小,建议不超过堆内存的1/2
  • -XX:MetaspaceSize 元空间
  • -XX:MaxMetaspaceSize 最大元空间
  • -Xss256k,线程栈大小,建议256k(java8建议1m)
  • -XX:PermSize=256m(java8后写成-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=64):永久代初始值,默认值为物理内存的1/64
  • -XX:MaxPermSize=256m:永久代最大值,默认值为物理内存的1/4
  • -XX:SurvivorRatio=8:年轻带中Eden区和Survivor区的比例,默认为8:1,即Eden(8),From Space(1),ToSpace(1)
  • -XX:MaxTenuringThreshold=15:晋升到老年代的对象年龄,每个对象坚持过一次MinorGC后对象年龄+1,默认值是15,年龄超过15进入到老年代,该参数在串行GC时有效
  • -XX:PretenureSizeThreshold=3145728:单位字节,只对Serial和ParNew两款收集器有效,新生代采用Parallel Scavenge GC时无效,大于这个值的对象直接在老年代进行分配
  • -XX:+UseConcMarkSweepGC:开启CMS垃圾回收器        

2.日志参数:

  •                     -XX:+HeapDumpOnOutOfMemoryError:当发生内存溢出时,进行堆内存dump
  •                     -XX:+PrintGCDetails:打印GC的详细信息

                    
 3.查看默认的堆大小及默认的垃圾收集器


                java -XX:+PrintCommandLineFlags -version

内存溢出如何分析


1.堆内存溢出,OutOfMemoryError:java heap space


                原因:
                    Java堆用于存储对象实例,只要不断创建对象,并保证GC Roots到对象间有可达路径避免这些对象的GC,当对象数量达到堆的最大容量限制后就会产生OOM
                解决方法:
                    通过参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath= 可以让虚拟机在内存溢出异常时Dump当前内存堆转储快照
                    通过内存映像分析工具(如:Eclipse Memory Analyzer,jvisualvm)对Dump出的堆转储快照分析,判断是内存泄露还是内存溢出
                    如果是内存泄露:通过工具查看泄露对象的类型信息和它们到 GC Roots 的引用链信息,分析GC收集器无法自动回收它们的原因,定位内存泄露的代码位置
                    如果是内存溢出:检查堆参数 -Xms和-Xmx,看是否可调大;代码上检查某些对象生命周期过长,持有时间过长的情况,尝试减少程序运行期间内存消耗
          

 2. 栈内存溢出,StackOverflowError


                原因:
                    StackOverFlowError异常:线程请求的栈深度大于虚拟机所允许的最大深度
                    OutOfMemoryError异常:虚拟机扩展栈时无法申请足够的内存空间
                解决方法:
                    检查代码中是否有死递归;配置 -Xss 增大每个线程的栈内存容量,但会减少工作线程数,需要权衡
          

 3.元空间内存溢出,OutOfMemoryError: Metaspace


                解决方法:
                -XX:MetaspaceSize 元空间
                -XX:MaxMetaspaceSize 最大元空间
            

4.本机直接内存(物理内存) NIO(input/output) NETTY  OutOfMemoryError: Direct buffer memory 不能够生成堆转成文件的
                -XX:MaxDirectMemorySize=

    

    

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值