jvm 的内存结构
jvm 是按照运行时数据的存储结构来划分内存结构的,jvm在运行java 程序时,将他们划分成几 种不同格式的数据,分别存储在不同的区域,这些数据统一称为运行时数据。运行时数据包括java 程序本身的数据信息和jvm运行java 需要额外的数据信息。
jvm 运行时数据区
程序技术器-----线程私有
java 虚拟机栈--线程私有
本地方法栈-----线程私有
java堆----------线程公用
方法区----------线程公用
jvm 内存分配
保存参数、局部变量、中间计算过程和其他数据,退出方法的时候,修改栈顶指针就就可以把栈帧中的内容销毁。
栈的优点:存取数据比堆快,仅此与寄存器,栈数据可以共享
栈的缺点:存在栈中的数据大小,生存期是在编辑时就确定的,导致其缺乏灵活性。
-Xss 每个线程使用的内存
堆的有点:动态地分配内存大小,生存期不必事先告诉编辑器,它是在运行期动态动配分配的垃圾回收器会自动收走不在使用的空间区域
堆内存机构如下图:
年轻代:有eden 区和from 、to 组成,可以是说是e 区s1 s2 区之间的工作模式是开始线程全部进入eden 区域会后会通过copy 工作模式把需要的线程copy 到s1 或者s2 区域所有通过new 创建的对象内存都在堆中分配。其大小可以通过-Xms -Xmx 来控制,堆被划分为新生代和旧生代,新生代又被进一步划分为Eden 和Suruvivor 最后和Survior由formspace 和tospace 组成。
老年代大小=堆内存-新生代-持久带 因为老年代没有参数可控制所以新生态越大老年代越小。
java 堆结构和垃圾回收
说明:Non-Heap 是非堆内存信息
Heap 堆内存信息也是新生代信息
jvm 堆配置参数
1、-Xms 初始化堆内存大小 默认是物理内存的1/64 (<1GB)
2、-Xmx 最大的堆大小 默认是物理内存的1/4(1<1GB),实际中建议大于4Gb
3、一般建议设置-Xms=-Xmx 好处是避免每次gc 后调整堆内存大小,减少系统内存分配的开销
4、整个堆大小=年轻代大小+老年代大小+持久代大小
jvm 新生代(young generation)
1、新生代=1和ede区 和两个subrvivor区
2、-Xmn 年轻代大小(1.4版本之前通过 -xx:NewSize ,-XX:MaxNewSize)
3、-XX:NewRatio
年轻代与老年代的比值(除去持久代)
Xms=Xmx 并且设置了Xmn 的情况下,该参数不需要进行设置
4、-XX:SurvivorRatio
Eden区与Survivor区的大小比值,设置为8,则两个Survivor 区与一个eden 区比值为2:8 Survivor区占整个年轻代的1/10
5、用来存放java 对象刚分配的java 对象
jvm 老年代(teunred geneation)
1、老年代=整个堆-年轻代大小-持久代大小
2、年轻代中经过垃圾回收没有回收掉的对象被复制到老年代
3、老年代存储对象比年轻代年龄大的多,而且不缺乏大对象
4、新建的对象也有可能直接进入老年代
4.1 大对象,可以通过启动参数设置-XX:PretenureSizeThreshold=1024(单位字节,默认为0)来代表超过多大时就不再新生代分配。而是直接在老年代分配
4.2 大的数组对象,切数组中无引用外部对象
java 强引用 | java 弱引用 |
不会丢失数据,所以访问速度快 线程开启,停止服务也没有办法回收线程除非重启系统 同时如果线程数到达java 设定的而无法进行处理那么 服务就会冗机 |
丢失数据,会去数据库读取数据会慢一些,如果eden区和s1 、s2 到old 区50M 如果old区缓存为40M在理想 的情况下正常数据从eden s1 s2 区到old 过来的数据不会超过1M 假如:老年代缓存为50M 这是空闲空间为5M 这时如果从新生代过来的数据超过5M 那么系统会直接回收所有的空间,用来存放新的数据,缓存的数据会读取数 库,如果是强引用系统就会挂掉。 |
5、老年代大小无配置参数
java 持久代(perm generation)
1、持久代=整个堆-年轻代大小-老年代大小
2、-XX:PermSize -XX:MaxPermSize
设置持久代的大小,一般情况推荐把-XX:PermSize设置成-XX:MaxPermSize的值为相同的值,因为持久代大小的调整也会导致堆内存需要发出fgc
3、存放Class、Method元信息,其大小与项目的规模;类、方法的数量有关,一般设置为128M 就足够了,设置原则是预留30% 的空间。
4、永久代的回收方式
4.1、常量池中的常量,无用的类信息、常量的回收很简单、没有引用了就可以被回收
4.2、对于无用的类进行回收。必须保证3点:
类的所有实列都已经被回收
加载类的class loader 已经回收
类对象的class对象没有被引用(即没有通过发射引用该类的地方)
jvm 垃圾收集算法
1、引用计数算法
每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0 可以收回,次方法简单无解决对象相互循环引用的问题,还有一个问题是如果解决精准计数
2、根据搜索算法
从GC Rots 开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GCRoots 没有任何引用链相连时,则证明此对象是不可用,不可到达对象
在java 语言中,GCRoots 包括
虚拟机中引用的对象
方法区类静态属性实体引用的对象
方法区类静态属性实体引用的对象
方法区常量引用的对象
本地方法栈中JNI 引用的对象
jvm 垃圾回收算法
复制算法(copying)
标记清除算法(Mark-weep)
标记整理压缩算法(Mark-Compac)
以下是重点*****
名词解释
1、穿行回收
gc 单线程内存回收,会暂停所有用户线程
2、并行回收
手机是指多个gc 线程并行工作,但此用户线程是暂停,所有sebul 是串行的,parallel (并行)收集器是并行的而cms 收集器是并发的
3、并发回收
是指用户线程同时执行(不一定是并行可能是交替,但总体上在同时执行的)不需要停顿用户线程,(其实在cms 中用户线程还是需要停顿的,只是非常短,GC线程在另一个CPU 上执行)
穿行 并行 并发 ****G1 不使用
新生代Serial 回收器
1、-XX:+UseSerialGC来开启 Serial New + Serial Old 的收集器组合进行内存回收
2、使用复制算法。
3、独占式的垃圾回收
缺点:一个线程进行GC 穿行。其他工作线程暂停
老年代Serial 回收器
1、-XX:+UseSerialGC 来开启Serial New+Serial Old 的收集器组合进行内存回收
2、使用标记--压缩算法
3、串行的、独占式的垃圾回收器
因为内存比较大原因,回收比新生代慢
新生代ParNew 回收器
1、-XX:+UseParNewGC 开启新生代使用并行回收收集器,老年代使用串行收集器
2、-XX:ParallelGCThreads 指定线程数默认最好与CPU数量相当,避免过多的线程数影响垃圾收集性能
3、使用复制算法
4、并行的、独占式的垃圾回收器
新生代Parallel Scavenge 回收器
1、吞吐量优先回收器
关注CPU 吞吐量,即运行用户代码的时间/总时间,比如:JVM 运行100分钟,其中运行用户代码99分钟,垃圾收集1分钟,则吞吐是99%,这种收集器能最高效率的利用CPU,适合运行后台运算
2、-XX:UseParallelGC 开启
使用Parallel Scaveng+Serial Old 收集器组合回收垃圾,这也是在Server 模式下的默认值
3、-XX:GCTimeRatio
来设置用户执行时间占总时间的比例,默认99 即1%的时间用来进行垃圾回收
4、-XX:MaxGGFCPauseMilllis
设置GC的最大停顿时间
5、使用复制算法
老年代Parallel Old 回收器
1、-XX:+UseParallel Old 组合收集器进行收集
2、使用标记整理算法
3、并行的、独占式的垃圾回收器
******互联网公司所用
cms (并发标记清楚)回收器
1、标记-清楚算法
同时他又是一个使用多线程并发回收的垃圾收集器
2、-XX:ParallellCMSThreads
手工设定cms 的线程数量,cms 默认启动线程数是(ParallelGCThreads+3/4)
3、-XX+UseConcMarkSweepGC 开启
使用ParNew+CMS+Serial Old 的收集器组合进行内存回收,Serial Old 作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用
4、-XX:CMSInitiatingOccupancyFraction
设置CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认值为68%,仅在cms收集器时有效-XX:CMSInitiatingOccupancyFraction=70
5、-XX:+UseCMSCompactAtFullCollection
由于CMS 收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMs 收集器有效时有效
6、-XX:+CMSFullGCBeforeCompaction
设置CMS 收集器进行若干次垃圾收集后在进行一次内存碎片整理过程,通常与UserCMSCompactAtFullCollection参数一起使用
7、-XX:+CMSInitiatingPermOccupancyFraction
设置perm Gen 使用到达多少比率时触发,默认是92%
下面是生产例子:(需要根据实际情况部署)
[[email protected] bin]# cat setenv.sh #!/bin/sh export JAVA_HOME=/usr/local/jdk1.7.0_60/ export JRE_HOME=/usr/local/jdk1.7.0_60/ JAVA_OPTS="-server -Xms5G ## 堆内存初始值 -Xmx5G ##堆内存最大值 -Xmn2G ##新生代空间 -Dsun.java2d.noddraw=true ##如果硬件加速已经被enable,可以通过这个选项来提高Swing GUI速度,默认值为false -XX:PermSize=500M ## 表示非堆区初始内存分配大小,其缩写为permanent size(持久化内存) -XX:MaxPermSize=500M ##-XX:MaxPermSize:表示对非堆区分配的内存的最大上限。 -Xss256k ##设置每个线程的堆栈大小 -XX:MaxTenuringThreshold=0 ##在新生代中对象存活次数(经过MinorGC次数)后仍然存活,就会晋升到旧生代 -XX:+UseParNewGC ##设置年轻代多线程收集,可与CMS 收集同时使用。在serial 基础上实现的多线程收集器 -XX:+UseConcMarkSweepGC ##并发标记清楚(CMS)收集器:cms 收集器 也被称为短暂停顿并发收集器。它是对年老代进行垃圾收集的。因为是多线程进行垃圾收回,可与减少停顿时间 -XX:+UseCMSCompactAtFullCollection ## 表示触发FGC 之后进行压缩,因为CMS 默认不压缩空间的 -XX:CMSFullGCsBeforeCompaction=0 ##,在上一次CMS并发GC执行过后,到底还要再执行多少次full GC才会做压缩。默认是0,也就是在默认配置下每次CMS GC顶不住了而要转入full GC的时候都会做压缩。 把CMSFullGCsBeforeCompaction配置为10,就会让上面说的第一个条件变成每隔10次真正的full GC才做一次压缩(而不是每10次CMS并发GC就做一次压缩,目前VM里没有这样的参数)。这会使full GC更少做压缩,也就更容易使CMS的old gen受碎片化问题的困扰。 -XX:+CMSClassUnloadingEnabled ##如果你启用了CMSClassUnloadingEnabled ,垃圾回收会清理持久代,移除不再使用的classes。这个参数只有在 UseConcMarkSweepGC 也启用的情况下才有用 -XX:-CMSParallelRemarkEnabled ##降低标记停顿 表示并行remark -XX:CMSInitiatingOccupancyFraction=90 ##使用cms作为垃圾回收,使用70%后开始CMS收集,为了保证不出现promotion failed(见下面介绍)错误 -XX:SoftRefLRUPolicyMSPerMB=0 ##在最后一次引用时,软可达对象将保持一定的时间 等待多少秒 -XX:LargePageSizeInBytes=128M ##设置用于Java堆的大页面尺寸 -XX:+UseFastAccessorMethods ##原始类型的快速优化 -XX:+UseCMSInitiatingOccupancyOnly ##使用手动定义初始化定义开始CMS收集 -XX:+PrintClassHistogram ##在垃圾收集之前执行 -XX:+PrintGCDetails ##GC 输出 -XX:+PrintGCTimeStamps ##GC 输出 -XX:+PrintHeapAtGC ##打印GC前后的详细堆栈信息 -Xloggc:${CATALINA_HOME}/logs/gc.log" ##把相关日志信息记录到文件以便分析.与上面几个配合使用 |