JVM理解及zabbix监控tomcat

简介:

背景

由于我们的tomcat出现过内存溢出的情况,由此分析一下内存溢出的原因
tomcat内存溢出有三种情况:
1.堆内存(Heap)溢出
2.持久代空间(Perm Gen)溢出
3.无法创建新线程
由此又引出Heap和Perm Gen是什么的问题
所以深入的学习理解一下JVM的知识,不过JVM需要学习的东西太多了,我只是学习了皮毛,在此记录一下,以后还要继续学习

JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。
JVM体系分为三部分:类装载器子系统、运行时数据区、执行引擎

类装载器

每一个Java虚拟机都由一个类加载器子系统(class loader subsystem),负责加载程序中的类型(类和接口),并赋予唯一的名字。每一个Java虚拟机都有一个执行引擎(execution engine)负责执行被加载类中包含的指令。JVM的两种类装载器包括:启动类装载器和用户自定义类装载器,启动类装载器是JVM实现的一部分,用户自定义类装载器则是Java程序的一部分,必须是ClassLoader类的子类。

执行引擎

主要的执行技术有:解释,即时编译,自适应优化、芯片级直接执行其中解释属于第一代JVM,即时编译JIT属于第二代JVM,自适应优化(目前Sun的HotspotJVM采用这种技术)则吸取第一代JVM和第二代JVM的经验,采用两者结合的方式 。

运行时数据区

我们重点学习的是运行时数据区的内容
运行时数据区又包括:方法区、堆、Java虚拟机栈、程序计数寄存器和本地方法栈
JVM理解及zabbix监控tomcat
方法区和堆由所有线程共享,也就是每一个进程会有独立的内存空间进行活动,之后的每个线程都共享这一块内存。
Java虚拟机栈、程序计数寄存器是由线程独享的,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储。每条线程包含一个栈区,只保存基础数据类型的对象和自定义对象的引用,每个栈区相互独立,互不干扰。

堆内存(Heap)

这就是我们需要关注的地方了,tomcat内存溢出的原因之一
堆内存分为三部分:Eden Space、Survivor Space、Tenured Gen
Eden Space和Survivor Space统一称作年轻代(young Generation)
Tenured Gen被称为老年代
Eden Space:对象被创建的时候首先放到这个区域,内存大小相对较小,GC频繁,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。
Survivor Space:用于保存在eden space内存区域中经过垃圾回收后没有被回收的对象。Survivor有两个,分别为To Survivor、 From Survivor,这个两个区域的空间大小是一样的。执行垃圾回收的时候Eden区域不能被回收的对象被放入到空的survivor(也就是To Survivor,同时Eden区域的内存会在垃圾回收的过程中全部释放),另一个survivor(即From Survivor)里不能被回收的对象也会被放入这个survivor(即To Survivor),然后To Survivor 和 From Survivor的标记会互换,始终保证一个survivor是空的。
JVM理解及zabbix监控tomcat
年轻代中执行的垃圾回收被称之为Minor GC(因为是对年轻代进行垃圾回收,所以又被称为Young GC),每一次Young GC后留下来的对象age加1。年轻代的空间越小,GC的频率就越高。
Tenured Gen:用于存放年轻代中经过多次垃圾回收仍然存活的对象,也有可能是年轻代分配不了内存的大对象会直接进入老年代。经过多次垃圾回收都没有被回收的对象,这些对象的年代已经足够old了,就会放入到老年代。当老年代被放满的之后,虚拟机会进行垃圾回收,称之为Major GC。由于Major GC除并发GC外均需对整个堆进行扫描和回收,因此又称为Full GC。GC相对不频繁。
所以Heap区也就是堆内存的大小=年轻代+老年代=Eden Space+Survivor Space+Tenured Gen 这三部分的大小
然后我们来说一说堆内存溢出了怎么办
JVM初始分配的堆内存由-Xms指定,这个参数在tomcat下的bin/catalina.sh文件中定义,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。Heap Size最大不要超过物理内存的80%
例如:JAVA_OPTS='$JAVA_OPTS -server -Xms128m -Xmx128m',一般为2的n次幂($JAVA_OPTS是保留原先设置)
还可以指定Young Generation的大小-Xmn,也就是Eden Space+Survivor Space的大小,一般为-Xmx的3、4分之一
如果堆内存空间还是不够的话,就只能加物理内存了

非堆内存

Java 虚拟机管理堆之外的内存都属于非堆内存,主要分析了Code Cache和Perm Gen两部分
Code Cache:代码缓存区,它主要用于存放JIT所编译的代码。CodeCache代码缓冲区的大小在client模式下默认最大是32m,在server模式下默认是48m,这个值也是可以设置的,它所对应的JVM参数为ReservedCodeCacheSize 和 InitialCodeCacheSize,可以通过如下的方式来为Java程序设置
-XX:ReservedCodeCacheSize=128m CodeCache缓存区是可能被充满的,当CodeCache满时,后台会收到CodeCache is full的警告信息
Perm Gen:Permanent Generation space,是指内存的永久保存区域,因而称之为永久代。这个内存区域用于存放类定义、字节码、常量等很少变更的信息,Class在被加载的时候被放入这个区域。默认大小为物理内存的1/64。这个区域一般被认为是运行时数据区中的方法区
Perm Gen这个空间不足就是造成我们tomcat内存溢出的第二个原因了
GC不会在主程序运行期间对Perm Gen进行清理,但如果程序加载的类太多了,或者使用了大量的第三方jar包,其大小超过了jvm默认的大小,就有可能会造成内存溢出。
JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由-XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。
造成tomcat内存溢出的第三个原因:无法创建新的线程
(这种现象比较少见,也比较奇怪,主要是和jvm与系统内存的比例有关。
这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给JVM的内存越多,那么,上述错误发生的可能性就越大。
每一个32位的进程最多可以使用2G的可用内存,因为另外2G被操作系统保留。这里假设使用1.5G给JVM,那么还余下500M可用内存。这500M内存中的一部分必须用于系统dll的加载,那么真正剩下的也许只有400M,现在关键的地方出现了:当你使用Java创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在余下的400兆内存里创建这个物理线程,而不是在JVM的1500M的内存堆里创建。在jdk1.4里头,默认的栈大小是256KB,但是在jdk1.5里头,默认的栈大小为1M每线程,因此,在余下400M的可用内存里边我们最多也只能创建400个可用线程。)这是我从网上看到的,我的理解就是创建线程的时候不光在JVM里创建,还要在操作系统里创建一个真正的线程,然后这个真正的线程是占用物理内存的,线程很多的情况下,物理内存就不够用了。

类加载

我们在用zabbix监控tomcat的时候不光要监控jvm内存的使用情况,刚才说到加载的类太多会造成Perm Gen这个空间不足,所以还要监控java类加载的情况;tomcat线程数太多也会造成问题,还要监控tomcat线程情况
java文件在代码编译后,就会生成JVM(Java虚拟机)能够识别的字节码文件(.class)。而JVM把Class文件中的类描述数据从文件加载到内存,也就是方法区,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称链接。类用到才被加载。JVM理解及zabbix监控tomcat

类卸载

类的生命周期

当我们编写一个java的源文件后,经过编译会生成一个后缀名为class的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在java虚拟机中运行,java类的生命周期就是指一个class文件从加载到卸载的全过程。
Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的。由用户自定义的类加载器加载的类是可以被卸载的。

tomcat线程

tomcat每一个进来的请求都需要一个线程,直到该请求结束。tomcat服务器每个实例就是一个进程,默认一个大线程池用于运行所有的webapp。在tomcat的conf/server.xml文件中可以定义maxThreads,就是实际可同时处理的请求数,默认为200;还有acceptCount,当同时连接的人数达到maxThreads时,还可以接收排队的连接,超过这个连接的则直接返回拒绝连接。

zabbix监控tomcat

服务端配置

Zabbix2.0起添加了支持用于监控JMX应用程序的服务进程,称为“Zabbix-Java-gateway”,它是用java写的一个程序。
1.安装Zabbix-Java-gateway,把他安装到/etc/zabbix目录下
2.修改Java-gateway的配置文件并启动它(zabbix_java_gateway.conf)

LISTEN_IP="0.0.0.0"       #监听地址
LISTEN_PORT=10052      #监听端口
START_POLLERS=5        # 开启的工作线程数(必须大于等于后面zabbix_server.conf文件的StartJavaPollers参数)

启动

service zabbix-java-gateway start

3.修改zabbix_server的配置文件并重启(zabbix_server.conf)

JavaGateway=127.0.0.1                     # JavaGateway 服务器地址,zabbix_server与zabbix_java_gateway在同一台主机
JavaGatewayPort=10052                    #端口
StartJavaPollers=5
#重启zabbix_server
/etc/init.d/zabbix_server restart

客户端配置

然后配置被监控端,开启JMX
1.下载catalina-jmx-remote.jar
wget http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.56/bin/extras/catalina-jmx-remote.jar #我的tomcat版本是7.0.56
将下载后后的jar包放到被监控的tomcat实例的lib目录下。
2.修改tomcat的server.xml文件,添加下面这句,定义JMX端口
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="13373" rmiServerPortPlatform="13374" />
我一开始从网上查到的端口信息要加到下面的catalina.sh文件那句话里,但是后来测试的时候 jmx始终报红,可是防火墙也已经开放端口了,后来我又查到,除了定义好的端口之外,JMX还会随机开放一个端口用来获取数据,所以两个端口就定义到server.xml文件中,在防火墙开放这两个端口就可以了
3.修改tomcat/bin/下的catalina.sh,添加如下内容:
CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=ip地址"
hostname为要获取数据的被监控端ip
4.重启tomcat
5.注意添加防火墙规则,添加server.xml中定义的端口
6.测试是否可以获取数据
命令行下测试需要cmdline-jmxclient-0.10.3.jar这个包,在服务端测试是否可以拿到数据
java -jar cmdline-jmxclient-0.10.3.jar controlRole:tomcat ip:端口 java.lang:type=Memory NonHeapMemoryUsage
注意cmdline-jmxclient-0.10.3.jar所在的路径,我是在root目录下测试,jar包也在root目录下,如果能拿到数据,那么配置就没问题了

zabbix管理页面

浏览器连接到zabbix服务器页面
1.新建模板
zabbix自带有监控JMX模板,但是很多都没有用,然后就自己新建了一个模板,监控堆内存,非堆内存,tomcat线程等等
配置tomcat线程的键值需要访问tomcat的端口号,于是这个不是加载模板里的,是一个一个复制的
JVM理解及zabbix监控tomcat
JVM理解及zabbix监控tomcat
2.创建主机
由于键值不能重复,所以每一个tomcat都作为一个主机,加到对应的机器组里
JVM理解及zabbix监控tomcat
3.连接模板
新建主机的时候就连接模板
4.新建触发器
设置了内存超过85%就报警
JVM理解及zabbix监控tomcat
5.新建图形
新建图形,添加监控项
JVM理解及zabbix监控tomcat
6.测试
如果监控项显示不支持的话,看一下键值是否正确,到服务器上测试是否能获取到数据,也有可能是加载时间较慢,耐心等待一下就有了,然后出来图形就可以监控了










本文转自 xinsir999 51CTO博客,原文链接:http://blog.51cto.com/xinsir/2057660,如需转载请自行联系原作者
目录
相关文章
|
3月前
|
存储 SQL 监控
修改Zabbix源码实现监控数据双写,满足业务需求!
虽然对接Elasticsearch后有诸多好处,但是它不往数据库写历史数据了,同时还不再计算趋势数据了。有这么一个场景...
修改Zabbix源码实现监控数据双写,满足业务需求!
|
3月前
|
Arthas 监控 Java
Arthas 可以用于监控和诊断在 Windows 系统下部署的 Tomcat 服务
Arthas 可以用于监控和诊断在 Windows 系统下部署的 Tomcat 服务
163 2
|
4月前
|
数据采集 监控 数据库
OceanBase社区版可以通过Zabbix监控
OceanBase社区版可以通过Zabbix监控
75 4
|
4月前
|
监控 关系型数据库 机器人
小白带你学习linux的监控平台zabbix
小白带你学习linux的监控平台zabbix
130 0
|
5月前
|
监控 数据可视化 Java
visualvm工具远程对linux服务器上的JVM虚拟机进行监控与调优
本文档主要总结在window本地环境远程对linux服务断的JVM虚拟机进行监控与调优的方法。
75 0
|
1月前
|
数据采集 监控 数据库
请问OceanBase社区版能否通过zabbix监控,然后将报错信息展现到grafana?
【2月更文挑战第25天】请问OceanBase社区版能否通过zabbix监控,然后将报错信息展现到grafana?
25 2
|
2月前
|
存储 监控 Java
JVM监控和分析技术在实践中可能会面临什么?
JVM监控和分析技术在实践中可能会面临什么?
|
2月前
|
监控 Cloud Native 关系型数据库
使用 Grafana 统一监控展示 - 对接 Zabbix
使用 Grafana 统一监控展示 - 对接 Zabbix
|
3月前
|
架构师 Java 关系型数据库
一线架构师开发总结:剖析并发编程+JVM性能,深入Tomcat与MySQL
每一个程序员都有自己清晰的职业规划和终极目标,无论之后是继续钻研技术,还是转管理岗、产品岗,都是需要自己具备有一定的实力,换句话说技术要牛逼。架构师,是很多程序员的终极目标,而成为一名Java架构师,那就需要对自己自身有一定要求,不仅技术能力要过硬,还需要有组织能力和提出解决方案的能力。那么作为架构师,需要掌握哪些技术呢?
一线架构师开发总结:剖析并发编程+JVM性能,深入Tomcat与MySQL
|
4月前
|
监控 Docker 容器
Zabbix【部署 03】zabbix-agent2安装配置使用(zabbix-agent2监控docker实例分享)
Zabbix【部署 03】zabbix-agent2安装配置使用(zabbix-agent2监控docker实例分享)
211 0

推荐镜像

更多