调优的目的是什么呢?
1.就是让系统更加的丝滑,让用户体验变得更好。
2.提升系统的性能,提高效率,充分利用jvm内存。
1.java.lang.OutOfMemoryError: Java heap space 解决方法,Java堆异常,需要配置jvm参数,尽量减少GC垃圾回收的次数
2.java.lang.OutOfMemoryError: Java heap space和java.lang.StackOverflowError:java.lang.OutOfMemoryError:java堆空间和栈溢出
这个问题的根源是jvm虚拟机的默认Heap大小是64M,可以通过设置其最大和最小值来实现.设置的方法主要是几个.
1.可以在windows 更改系统环境变量
加上
JAVA_OPTS=-Xms64m -Xmx512m
2.如果用的tomcat,在windows下,可以在
C:\tomcat5.5.9\bin\catalina.bat 中加上:
set JAVA_OPTS=-Xms64m -Xmx256m
位置在: rem Guess CATALINA_HOME if not defined 这行的下面加合适,或者是在setlocal下面添加,便于查看
3.如果是linux系统
Linux 在{tomcat_home}/bin/catalina.sh的前面,加
set JAVA_OPTS='-Xms64 -Xmx512'
spingboot项目启动,可在VM options项配置,tomcat修改catalina.bat(.sh),或者是配置系统环境变量均可,看你喜欢
对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
由上图可以清楚的看到JVM的内存空间分为3大部分:
堆内存 :所有new对象都放在堆里,这也是JVM优化的重点。
栈内存:也叫线程栈,是每个线程独立分配的内存空间,存储的包括局部变量,操作数,动态链接,方法出口。它和堆的关系其实是堆的指针引用。
本地方法栈:为虚拟机用到的Native方法服务。Native方法是java通过JNI直接调用本地C/C++库,可以认为是暴露给java的本地接口。
方法区:类装载系统会把class信息放到方法区,通过字节码引擎执行。会放常量(运行时常量池,那么什么是静态常量池),静态变量,类元信息。jdk8以后就叫元空间。
其中栈内存可以再细分为java虚拟机栈和本地方法栈,堆内存可以划分为新生代和老年代,新生代中还可以再次划分为Eden区、From Survivor区和To Survivor区。
其中一部分是线程共享的,包括 Java 堆和方法区;另一部分是线程私有的,包括虚拟机栈和本地方法栈,以及程序计数器这一小部分内存。
所有的new对象最开始都放到Eden(伊甸)区,当Eden区沾满之后,JVM会进行一次minor gc,将发现依然存活的对象放到Survivor区,同时讲分代年龄+1(存储在object header里面),会清理eden和form区域,通过复制等算法再次将存活对象放到to区域,依次类推来回复制,当年轻代年龄达到15时,会挪到老年代,比如静态变量,数据库连接池,缓存就属于“老不死”。当老年代内存被占满时,就会触发full gc,full gc会对整个堆进行垃圾回收,因此非常耗时,时间很长,我们要尽量降低full gc的频次。
full gc:完整垃圾回收,整个堆进行垃圾回收
minor gc:局部回收,参考下图,仅回收新生代中的Eden
注意:old区内存满了后,就会触发full gc
minor gc如何进行垃圾回收呢,这里面运用到一个可达性分析算法,里面有一个GC roots根的概念很关键,要理解一下,gc会根据该算法找出该对象的整条GC root链,定义为有效存活对象,会移动到survivor区域;剩下的对象由于找不到对象引用,因此定义为无效对象,从而进行回收。具体如下
当old区内存满了后,就会触发full gc,但是发现引用还在(list还是运行),因此没法回收,但是死循环又一直在new对象,old区无内存可用,于是就OOM。
方法区也称"永久代",它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。
在JDK8之前的HotSpot JVM,存放这些”永久的”的区域叫做“永久代(permanent generation)”。永久代是一片连续的堆空间,在JVM启动之前通过在命令行设置参数-XX:MaxPermSize来设定永久代最大可分配的内存空间,默认大小是64M(64位JVM默认是85M)。
随着JDK8的到来,JVM不再有 永久代(PermGen)。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory。
方法区或永久代相关设置
典型JVM参数配置参考:
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大 小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000 左右。
jvm分析工具,安装的jdk的bin目录下
jdk1.8.0_31\bin
1.jvisualvm.exe
2.jconsole.exe
1.下载安装
点击下载VisualVM软件
由于我的是mac系统,因此我下载下面这个dmg文件,如果你是windows系统,下载上面那个zip文件,windows系统的是zip压缩包,解压即可,无需安装,找到bin目录下的visualvm.exe文件双击即可启动;如果是mac系统的dmg文件,需要下载后安装启动。
2.安装Visual GC插件
上面下载完成之后,解压缩包,执行文件夹里面bin下面的visualvm.exe应用程序
点击菜单Tools,选择Pulgins,在第二个选项中找到Visual GC,选中,点击Install,然后一路下一步即可安装成功,由于本人已经安装,所以未展示Visual GC,如下图:
安装完插件后重启软件,然后打开idea,随后便可以在左边了Local中看到启动的idea应用,双击即可进入监控页面,如下图:
右边就是Visual GC 插件的主要界面了,我们可以看到软件运行时的内存变化情况:
下面对上图中的各个窗口区域做简单介绍,整个界面分为三个区域,分别为:Spaces、Graphs和Histogram。
1.Spaces窗口
上图是呈现了程序运行时我们比较关注的几个区域的内存使用情况:
2.Graphs窗口
该窗口区域包含8个图标,以时间为横坐标动态展示各个指标的运行状态:
下面从上往下对上图中的各个图标表及其状态进行说明:
3.Histogram窗口
Histogram窗口是对当前正在被使用的Survivor区内存使用情况的详细描述,如下:
Threshold:我们知道Survivor区中的对象有一套晋升机制,就是其中的每个对象都有一个年龄标记,每当对象在一次Minor GC中存活下来,其年龄就会+1,当对象的年龄大于一个阈值时,就会进入老年代,这个阈值就是Tenuring Threshold,要注意这个值不是固定不变的,一般情况下Tenuring Threshold会与Max Tenuring Threshold大小保持一致,可如果某个时刻Servivor区中相同年龄的所有对象的内存总等于Survivor空间的一半,那Tenuring
Threshold就会等于该年龄,同时大于或等于该年龄的所有对象将进入老年代。
Threshold:表示新生代中对象的最大年龄值,这个值在JDK1.8中默认为6,在JDK1.7及之前的版本中默认为15,可以通过参数-XX:MaxTenuringThreshold来指定。
Survivor空间大小验证阈值(默认是survivor空间的一半),用于给Tenuring
Threshold判断对象是否提前进入老年代。
当前Survivor空间大小,单位为字节(Byte,B)。
表示Survivor中不同年龄段对象分布。