浅谈JVM

java编译运行过程:

编译期:.java源文件,经编译,生成.class字节码文件
运行期:JVM加载.class并运行.class

JDK、JRE、JVM

JVM:java虚拟机

  • 加载.class并运行.class

JRE:java运行环境

  • 除了包含JVM以外还包含运行java程序所必须的环境
    JRE=JVM+java系统类库

JDK:java开发工具包

  • 除了包含JRE以外还包含开发java程序所必须的命令工具
    JDK=JRE+编译、运行等工具

堆栈方法区

堆:

概述:

  1. 存储所有new出来的对象(包括实例变量)

  2. 垃圾回收器(GC)不定时到内存的堆中清扫垃圾
    回收的过程是透明的,不一定一发现垃圾就立刻回收
    调用System.gc()可以建议虚拟机尽快调度GC来回收

  3. 内存泄漏:不再使用的内存没有被及时的回收
    建议:对象不再使用时,及时将引用设置为null

  4. 内存溢出

  5. 实例变量的生命周期:
    创建对象时存在堆中,对象被回收时一并被回收

栈:

  1. 存储正在调用中的方法中的所有局部变量(包括参数)
  2. 调用方法时在栈中为该方法分配一块对应的栈帧,
    栈帧中包含方法的局部变量以及参数,
    方法调用结束时,栈帧被清除,局部变量一并被清除
  3. 局部变量的生命周期:
    调用方法时存在栈中,栈帧被清除时一并被清除

方法区:

  1. 存储.class字节码文件(包括方法、静态变量)
  2. 方法只有一份,通过this来区分具体的对象

JVM如何加载.class文件

  • 类的加载是由类加载器(ClassLoader)及其子类完成,它主要负责在运行时查询和装入类文件中的类:
  • 由于java的跨平台性,经过编译的java源程序不是一个可执行的程序,而是一个或多个类文件。类的加载是指把类的.calss文件中的数据读取到内存中,它会创建一个字节数组来读入.class文件,然后会产生与加载的类所对应的class对象,加载完毕后,这个class对象还是不完整的,这个类还是不可用的;接下来这个类会进入一个连接阶段,这个阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)、解析(将符号引用替换成直接引用)三个步骤;最后JVM对类进行初始化,包括:如果类存在直接的父类并且这个父类没有被初始化,那么就首先初始化父类;如果类中存在初始化语句,就会一次执行这些初始化语句。(读取到内存 - 连接阶段 - 初始化)
  • 这些过程都是通过类加载器完成的,包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)、自定义加载器(java.lang.ClassLoader的子类)。从jdk1.2以后,类加载得过程采用了父类委托机制(PDM),指的是在这个机制中,JVM自带的Bootstrap是根加载器,其他的加载器有且仅有一个父类加载器。类的加载首先请求父类加载器,父类加载器无能为力时候才由其子类加载器加载,
    • Bootstrap:负责加载JVM的核心类库
    • Extension:从系统指定的目录加载类库,父加载器是Bootstrap
    • System:应用加载器,父类是Extension,它会从环境变量classpath或者系统属性所指定的目录中加载类,是用户自定义加载器的默认父级加载器

JVM 调优

首先可以去了解一下新生代、老年代、永久代:
垃圾回收机制

  1. 针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值;

    • -Xms2g:初始化推大小为 2g;
    • -Xmx2g:堆最大内存为 2g;
    • -XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
    • -XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
    • –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
    • -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
    • -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
    • -XX:+PrintGC:开启打印 gc 信息;
    • -XX:+PrintGCDetails:打印 gc 详细信息。
  2. 年轻代和年老代将根据默认的比例(1:2)分配堆内存, 可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代。

    比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。

  3. 年轻代和年老代设置多大才算合理

    • 更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC

    • 更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率

    如何选择应该依赖应用程序对象生命周期的分布情况: 如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性。

    在抉择时应该根 据以下两点:

    • 本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 。

    • 通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1但应该给年老代至少预留1/3的增长空间。

  4. 在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC 。

  5. 线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。

理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值