程序员的自我修养
Home » Java语言 » gc老生常谈

gc老生常谈

0条评论679次浏览

算是笔记吧。看了很多次的jvm、gc,但都没记住。这次遇到了自己写得线上服务出现了oom+死锁+频繁full gc(扶额),总算是记住了jvm和gc的基础知识。也是醉了。

jvm模型

主要分3个模块吧:堆、栈、本地方法栈。

堆=young(eden + survivor(from + to)) + tenured + permanent,也有说permanent不属于堆的,不过从gc日志来看我更倾向前者,但从gc参数来看应该是后者。

结合java8的gc来看:

young

par new generation就是新生代young,其大小等于eden区+from/to区,注意这里不是eden+from+to。为什么呢?因为你永远不能同时使用from和to。eden、from和to的比例默认是8:1:1,可以通过参数-XX:SurvivorRatio调节。

新对象的分配发生eden区域内,当新对象(非大对象)无法在该区域分配时便引发Minor GC。大对象放不下的时候直接去tenured区分配。Minor GC就是将eden+from/to的存活对象copy到to/from中去,顺带存活了若干次的会放到tenured区,而且放不下的也会去old区。

tenured

concurrent mark-sweep generation就是老年代tenured,这里因为gc用了CMS,所以才显示成concurrent mark-sweep generation的吧?young和tenured比例通过-XX:NewRatio调节。

前面提到的“大对象/放不下/Minor GC中存活若干次”是对象进入tenured的三种方式。当tenured发生垃圾回收的时候就是full gc。stop the world!Minor GC也是stop the world!

permanent

Metaspace应该是java8里面才这么叫?我记得以前好像不是叫Metaspace。反正意思就是permanent永久代。permanent区不够用的时候触发的也是full gc。

permanent存放了要加载的类信息、静态变量、final类型的常量、字符串常量、属性和方法信息。-XX:MaxPermSize可以调节大小,默认好像是64MB。

注意new String("xx")是放在eden区的!只有String a = "xx"才是放在该区域。

每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果。无限递归的时候都知道是什么错吧。

本地方法区

顾名思义,就是放一些native方法的地方,比如如果用netty,各种native方法就是放这里。

gc参数

Young

-XX:+UseSerialGC

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:+UseParNewGC

Tenured

-XX:+UseSerialGC

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:+UseConcMarkSweepGC

-Xmx 堆最大大小

-Xms 堆初始大小

-Xmn 控制eden(新生代)大小

-XX:SurvivorRatio 控制eden和survivor比例

-XX:NewRatio 控制young与tenured比例

-XX:+PrintGCDetails 打印gc信息

-XX:+PrintGCTimeStamps

-XX:+PrintGCDateStamps

-XX:+PrintHeapAtGC gc时打印heap信息

-Xloggc: gc日志路径

-XX:MaxPermSize permanent最大大小

-XX:PermSize permanent初始大小

gc算法

新生代:Serial、Parallel Scavenge、ParNew。老年代:Serial Old、Parallel Old、CMS。G1应该是没有新生代老年代的说法了,具体还没看。

Serial是单线程模式下用,目前基本淘汰了吧。Parallel和CMS比起来,网上有如下结论:使用CMS应用的吞吐量会相对下降,但有更好的最差响应时间。如果你的应用对峰值处理有要求,而对一两秒的停顿可以接受,则使用(-XX:+UseParallelGC);如果应用对响应有更高的要求,停顿最好小于一秒,则使用(-XX:+UseConcMarkSweepGC)。

新服务oom、死锁、频繁full gc的排查

结论在前:oom的原因是有线程池没有关掉;频繁full gc是一次性加入到线程池的任务太多;死锁是LinkedBlockingQueue为空但我却用take去取对象。

服务中用到了netty,NioEventLoopGroup就是线程池。代码中对于正确完成的任务进行了关闭NioEventLoopGroup的操作,但是抛异常的任务却没有关闭。最终导致NioEventLoopGroup对象过多,线上运行1个月左右就会oom。

查找手段:jmap -histo:live jid发现NioEventLoopGroup和ApnsClient对象不是1:1,从代码来看一个ApnsClient有一个属性NioEventLoopGroup,正常情况应该是1:1。jstack -l jid发现非常非常多的NioEventLoopGroup线程存在,而实际上ClientCache里面根本没有缓存那么多的ApnsClient。最终判断出NioEventLoopGroup对象没有正常被回收,最终定位到代码抛异常时没有关闭NioEventLoopGroup。

频繁full gc的查找过程类似,在发送巨型任务时jmap一次查看到占用内存最多的对象都是线程池相关的,基本就得出结论了。一个有意思的事情是占用内存最多的是[C对象和String对象,而我的项目里面没有new String的代码,这2个对象个数基本是1:1,用String的总bytes除以对象个数正好是24,不论何时jmap,结果都是24。至今没有弄明白,难道是每个进入线程池的任务有一个长度为24的String类型的id?

(转载本站文章请注明作者和出处 程序员的自我修养 – SelfUp.cn ,请勿用于任何商业用途)
分类:Java语言
标签:,
发表评论


profile
  • 文章总数:79篇
  • 评论总数:331条
  • 分类总数:31个
  • 标签总数:44个
  • 运行时间:1072天

大家好,欢迎来到selfup.cn。

这不是一个只谈技术的博客,这里记录我成长的点点滴滴,coding、riding and everthing!

最新评论
  • 增达网: 受教了!呵呵!
  • Anonymous: :!: :smile: :oops: :grin: :eek: :shock:
  • 27: :razz: dsa会报错,rsa不会
  • Anonymous: 看错了 忽略我
  • Anonymous: UserSideCF这个类在哪里
  • 晴子: 在主节点初始化CM5数据库的时候报错误:Verifying that we can write to /opt/cm-5.9.0/etc/cloudera-scm -server log4j:ERROR Could not...
  • zhangnew: 就4题 :?:
  • linxh: “ 但要是遇到预先并不知道数组的长度而又需要获取正确的(或者称之 为原始的)split长度时,该如何处理呢。。? ” 印象中可以split函数参数传-1?
  • linxh: 班门弄斧一下: ssh host cmd 和直接ssh上后cmd结果不一样是因为ssh直接运行远程命令 是非交互非登录模式与ssh上去得到一个登录交互式Shell二 者加载的环境变量不一样。
  • 匿名: 其实文本分类和数字分类是一样的,只是文本分类需要多一个步骤, 就是计算它的tf-idf值将其转换为double类型
  • yurnom: 可能苹果最近又改变了返回值吧,最近没做测试了。 BadDeviceToken一般测试环境和正式环境弄错的情况 下会出现。
  • Anonymous: :razz: 博主,良心贴啊, 最近也在弄apns推送。 有个问题想请教你一下啊。 你博客中写的 Unregistered 错误,有准确的说明吗, 我看你博客中写的:...
  • 一波清泉: 回复邮箱: 1004161699@qq.com 多谢
  • Anonymous: 17/02/09 01:15:02 WARN Utils: Service ‘SparkUI’ could not bind on port 4040. Attempting port...
  • pacificLee: :twisted:
  • 小码: 为什么没有后面的呢,只有前10个
  • Anonymous: :lol:
  • Anonymous: :razz: 楼主是属于会聊天的。 我想问,sqoop发了几个版本了,应该没这些问题了吧。
  • Anonymous: Config.kafkaConfig.kafkaGroupI d 这个是指自己配置的group id 还是从 import org.apache.kafka.common.config .Config 这个类...
  • Anonymous: ZkUtils.getPartitionsForTopics (zkClient, Config.kafkaConfig.topic) 那个方法是在 spark-streaming_2.10 中 kafka...