java内存结构及jvm参数调优

内存结构

其中栈内存是每个线程私有的,存放自己的局部变量,在线程创建时被创建的,堆内存存放new关键字的对象,会被jvm的垃圾回收机制管理,是共享的,本地方法栈是用于调用c或者c++的驱动代码的,方法区用于存放常量、被加载的类的信息、静态变量等,是共享的
在这里插入图片描述

垃圾回收机制

  1. 不可达对象:对象没有被引用或没有存活
  2. jvm垃圾回收机制:jvm会不定时自动回收不可达对象的内存
  3. finalize:Object类中方法,在垃圾回收时会被调用

堆内存

堆内存结构

在这里插入图片描述

判断对象是否存活(可达)的算法

  1. 引用计数法:当对象被创建时,默认对象为0岁,gc线程会不定期进行自动回收,在回收时(包括Minor GC和Major GC)如果当前对象被引用就年龄+1,没有被引用就-1,年龄大于15放在老年代,小于15放在新生代,当年龄小于0就会认为对象不可达,被清理。但因为有循环引用的问题,所以已经被弃用
  2. gc根搜索算法(可达性分析算法):当通过方法区、栈出发,可以找到当前在堆内存中的对象,则认为这个对象是可达的,否则认为是不可达的,gc根搜索可以解决循环引用问题

根据清理不可达对象的垃圾回收算法

  1. 标记清除算法:在老年代中使用,0标记为存活,1标记为没有存活,使用这种方法时在进行垃圾清理时会出现内存碎片化的问题,造成内存浪费
    在这里插入图片描述
  2. 复制算法:在新生代中使用,当eden区对象存活,java会将其移到s0或s1中,当eden满了后,就执行垃圾清理,将所有可达的对象复制到另一个s区中,然后直接抹掉当前的s区中的数据,优点是可以解决内存碎片化问题,执行效率高,缺点是s0和s1始终只能有一个区存放数据,另一个是用于下次内存清理时复制可达对象时使用的
  3. 标记压缩算法:在老年代中使用,在标记清除算法之上,会整理碎片化内存空间,缺点是堆内存中原对象的引用地址会发生改变

Minor GC和Full GC

  1. 新生代GC即Minor GC,新生代中的对象不稳定,所以GC速度快,频率高
  2. 老年代GC即Major GC,老年代对象稳定,GC速度比新生代GC慢很多,频率也更低
  3. 老年代GC一般会伴随一次新生代GC,所以老年代GC又叫Full GC
  4. 新生代中的eden满时,触发新生代GC,survivor满不会触发GC
  5. 老年代满时,触发老年代GC,即Full GC

垃圾回收

可以手动设置的参数

  1. xms:初始化堆内存
  2. xmx:最大堆内存
  3. xmn:新生代最大内存
  4. 新生代与老年代的比例
  5. 新生代中eden区和s区的比例
  6. 堆内存大小首先按照xms的值进行初始化,当接近xms值时触发gc,当gc后内存还是不够就在xms基础上增加一点内存,直到增加的内存值达到xmx后,再增加就会抛出异常,在设置时,如果要想程序gc次数尽可能少,就可以使xms = xmx,程序初始内存就是最大内存,就能避免扩大内存时进行的gc

堆内存回收流程

当堆内存不够时,先触发新生代gc,新生代gc后还不够用触发full gc,full gc还不够用就抛异常

栈内存溢出

产生于方法的递归调用,当递归次数超过栈内存的最大深度就会发生栈内存溢出,xss参数可以用于指定栈内存深度

元空间和永久代

jdk8中用元空间取代了永久代,永久代之前由jvm进行分配,经常会出现内存溢出问题,而元空间改为直接使用操作系统的本地内存

内存溢出和内存泄漏的区别

  1. 内存溢出:指在运行程序前向jvm申请内存,但申请的内存超出了jvm能分配的最大内存,于是就出现内存溢出
  2. 内存泄漏:指程序原本在正常运行,但运行过程中没有去回收运行时产生的垃圾,产生的这种没有回收的垃圾就是内存泄漏,一次内存泄漏不会产生很大影响,但如果内存泄漏越来越多,内存空间就会被垃圾占完,最终导致正常运行的程序抛出异常

强引用和弱引用(针对栈空间中存放的变量名)

  1. 大部分引用都是强引用,当内存不足时,jvm会直接抛出异常也不会回收强引用所对应的堆内存对象的空间,若想要取消某个强引用与堆内存中对象的关联,直接将这个引用指向null,jvm就会在之后自动回收堆内存的这个对象
  2. 弱引用(WeakReference)在被jvm垃圾回收器扫描到时就会直接回收,无论当前是否需要gc,在ThreadLocalMap中的Key值就属于弱引用

ThreadLocal内存泄漏

ThreadLocal的实现原理就是为每个线程维护一个独立的ThreadLocalMap,而map中的Key就是弱引用的ThreadLocal实例,value为线程变量副本
在这里插入图片描述
ThreadLocalMap的生命周期和Thread一样,当Thread死亡后,其对应的ThreadLocalMap内存也应该被释放,在ThreadLocalMap中的Key值是一个弱引用,弱引用会在gc垃圾回收器扫描到时自动回收,所以我们在使用ThreadLocal时不用去手动清除ThreadLocalMap的Key所引用的内存,但ThreadLocalMap的value会通过强引用指向其他的变量,如果我们不手动清除ThreadLocalMap中map的引用,就会导致线程结束后,其value引用的空间不能被释放,发生ThreadLocal的内存泄漏,所以ThreadLocal的正确使用方法是在其销毁的时候调用remove方法,remove方法就是用来回收ThreadLocalMap中value部分的强引用所指向的空间的

垃圾回收器

jvm中由多种垃圾收集器

  1. 串行回收器:使用单线程进行回收,采用复制算法进行回收,垃圾回收过程中所有其他线程暂停
    在这里插入图片描述

  2. ParallelNew回收器:针对串行回收的改进,采用复制算法进行回收,只是将串行回收中的单回收线程改成多回收线程,在回收时也会暂停其他线程,但用时大大降低。jdk8中默认就是这种回收方法一般新生代并行复制回收、老年代串行标记压缩回收

  3. G1回收器:jdk9中默认就是这种回收方法,G1中不再将内存从物理上划分为年轻代和老年代,g1会在运行时动态调整新生代和老年代的区域大小,g1的内存结构如图
    在这里插入图片描述
    当出现了巨大的对象时,会将其放入Humongous区,当一个Humongous区还是存不下就触发gc,合并多个Humongous区,g1中针对年轻代的回收仍然会采用暂停应用线程的方式,通过将存活对象拷贝到survivor或老年代中进行年轻代区域的内存清理

    使用g1时通过3步完成调优
    1. 开启G1
    2. 设置堆的最大内存
    3. 设置最大停顿时间

jvm参数调优

主要调整的参数为年轻代、老年代、元空间及使用的垃圾回收器
主要结论:

  1. 在jdk8之后,在服务器性能较好的情况下优先使用g1垃圾回收器
  2. 每天如果出现超过1次的full gc说明有问题,先看是否是由于内存泄漏导致的,没有内存泄漏再调整jvm参数
  3. 让xms=xmx以减少gc的次数
  4. 使用并行垃圾回收器,并行比串行的回收效率更高
  5. 使用g1时通过3步完成调优
    1. 开启G1
    2. 设置堆的最大内存
    3. 设置最大停顿时间

Tomcat参数调优

普通ssm项目直接在tomcat的Catalina.dat文件中设置jvm的参数信息,调优原理同上

如果是针对springboot项目进行调优,可以通过在使用java -jar命令启动时,通过命令的方式配置jvm参数,同时可以在yml或properties文件中配置tomcat的连接池参数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值