Java程序员也应该知道的系统知识系列之内存

作者:林昊

上篇说到了Java程序和CPU的关系,对于多数实现的较好的Java应用程序而言,基本上随着CPU的核数增加或能力提升,系统能够支撑的并发量就可以稳步上升,但对于内存而言,是否也是这样呢,这篇我们就来看看Java程序和内存的关系。

 

和CPU一样,我们首先要知道机器上的内存的硬件状况,在linux下,可以通过dmidecode | grep -A16 “Memory Device$”命令来查看机器插了多少根内存条,以及每根内存条的具体型号,内存条的具体型号对Java应用的运行性能也会有些影响,但一般来说不会有CPU那么明显。

要查看机器上内存的使用状况,可通过free -m来查看,这个时候常见的第一个问题是看到free值很小,就认为内存不够用了,但其实真正可用的内存是free+buffers+cached,os为了提升运行性能,会利用一些内存来做cache,以提升诸如读写文件的速度等。

 

当free不够的时候,os会根据一个系统值来决定是释放buffers/cached还是使用swap,如果swap没开启就不用判断了,如果swap开启了,那么vm.swappiness这个值就非常关键了,这个值是一个倾向值的意思,值越大表示越倾向于使用swap,越小表示越倾向于释放buffers/cached,对于响应时间敏感的应用而言,只要用到swap了,通常对响应时间的影响都会很明显,而且swappiness默认是60,意味着默认其实是倾向于使用swap的,因此对于这类系统建议最好是关闭swap,毕竟对于集群型的应用来说,通常都是宁可接受内存不够用的情况下机器挂掉,也不能接受响应时间变慢。

 

对于cached的内存区域,可以执行echo 3 > /proc/sys/vm/drop_caches来强制释放,这种在某些情况下可能会需要用,例如希望把还在cache里的文件内容刷到磁盘。

 

对于swap区域,可以通过执行swapoff -a来强制刷掉,如果需要再开启,可以执行swapon -a。

 

除了os利用内存来提升运行性能外,cpu也同样借助它的各级cache来提升运行速度,多核之后,UMA的方式导致系统总线带宽会比较吃紧,而NUMA是解决这个的一种好的方式,关于NUMA具体是什么就不在这里讲了,需要知道下的是默认通常是不打开NUMA的,从我们的一些测试来看,有些CPU型号在是否打开NUMA的情况下应用的性能会相差一倍,不过大部分的CPU型号里打开NUMA的提升大概会在20%–30%左右,如果OS没打开NUMA,其实在Java启动参数上设置了-XX:+UseNuma也是没什么用的,可以用numactl -H来查看NUMA是否打开,但由于打开NUMA的话对应用跑在同一个NUMA Node上要求还是比较高的,因此在虚拟机类的场景中为了追求CPU搭配的灵活性以及维护的简便性,通常就只能放弃NUMA了。

 

要看运行的Java进程消耗的内存,可以用ps aux | grep java或具体的pid、或top -p [pid]也可以看,可以看到的是有两列内存的信息,一列是VIRT,一列是RES。

 

VIRT表示的是此进程占用的地址空间的大小,地址空间在32bit的os上的上限是3G,在64bit可以认为是无限大,当地址空间不够用的时候,Java进程会直接crash,在crash的log里会有java.lang.OutOfMemoryError: Out of swap space的信息,Java进程在启动时会根据-Xms + -XX:PermSize先申请好相应大小的地址空间,在创建线程等的时候也会直接申请好-Xss对应大小的地址空间,所以创建了很多线程的情况下可以看到VIRT会很高,

 

RES表示的是此进程具体占用的内存的大小,这个地方很容易产生两个疑问:
1. 为什么看到的RES值大于或小于了-Xmx的设置;
Java应用在刚启动,或者说还没有到触发Full GC之前,只有当真正需要使用内存才会去占用实际的内存,否则只是占据了地址空间,因此看到的RES值有可能会小于-Xmx的值;
而对于一个运行了一段时间且触发过CMS GC/Full GC的Java应用而言,则很有可能看到的RES大于了-Xmx的值,原因在于Java除了-Xmx会占用相应的内存外,Perm Gen、C Heap(CodeCache、Direct Memory、线程、对象结构、GC等)也要占据一些内存,所以看到的RES大于-Xmx也很正常。

 

2. 为什么GC后RES的值没下降相应的数值;
这个的原因在于GC后JVM并不会把内存释放给OS,而是会占着继续用。

 

Java程序在运行中过程,除了Direct Memory、直接用Unsafe操作、或间接的使用Deflater等的会涉及到C Heap,更多的是去JVM Heap中申请内存,并且由于JVM包装掉了,所以Java程序员在写代码的时候很容易由于错误的使用API或数据结构导致内存的浪费,这通常是为什么很多C的高手(注意:这里说的是C的高手)写的代码效率会比普通的Java程序员写的高不少的一个原因之一,而回收也由JVM来控制,这个系列的文章主要是科普下系统方面的知识,JVM的一些就不在这里写了,在之前的一些PPT或文章里也写过很多次关于JVM的内存管理,同样关于怎么去查Java程序在JVM Heap和C Heap里的消耗,之前也写过不少的文章,就不在这里写了,毕竟这些多数和系统关系就不算大了。

 

关于内存资源这块,Java程序倒不一定是越多越好,内存越大,通常也就意味着GC的负担越重,而GC的时候通常应用是全暂停的(除了CMS是Almost Concurrently外),但也不能太小,太小的话运行时会比较明显的暴露出来,因为会导致非常频繁的GC(到底多频繁算频繁呢,从目前的经验来看,ygc尽可能能在3s+一次,fgc或cms gc的话最好在10分钟以上),而太频繁的GC会导致CPU大部分时候都耗了执行GC上,应用能够支撑的并发量自然就会不够,够用就OK,在排除内存泄露等因素外,可以看看在Full GC后实际需要占用的内存大小,一般来说只要确保给Java进程留有的空间比这个需要常驻的大小大一定比例就OK(不过到底大多少还真不好说,凭经验吧),不要因为机器内存有多(相对而言,现在多数机器在内存这块都是比较够的),就给Java分配更多的内存,否则一次较长时间的暂停搞不好就回导致极大的杯具,所以内存资源这块和CPU不太一样,我的观点一向是够用并留有一定空间就OK,而不用去追求用满,当然如果能充分有效的利用多余的内存提升性能当然是OK的,例如cache什么的。

 

从内存资源的状况可以看到,随着硬件的不断发展,将来对Java应用而言,会有个悲催的现象是,CPU用的比较满,但机器的内存资源浪费的比较严重,针对这个问题,看来后面必须专门写一篇来讲讲虚拟化。

 

说到这了,顺带说下上篇文章留下的一个话题,就是GC这种线程在执行的时候是怎么确保占有足够的时间片,这个的原因是GC在执行的时候其他的线程其实都是处于暂停状态(其实这话不太准确),GC要执行前,JVM会先将一个内存页设为只读,而在所有有引用关系赋值的地方,JVM在编译代码时都会先插入一个检查某个内存页的状态的代码,而因为之前GC已经把这个内存页状态设为了只读,所以当其他线程的代码走到这个地方的时候,会抛出异常,从而导致线程进入一个blocked的状态,就不会来抢占GC线程需要的CPU了。

时间: 2024-09-23 03:37:04

Java程序员也应该知道的系统知识系列之内存的相关文章

Java程序员也应该知道的系统知识系列之全局体系

作者:林昊   Java程序部署到服务器上后,要被外部用户访问到,还需要一系列的东西组成,这些东西也是需要了解的.   外部用户要访问,通常来说都会给个域名,有域名就意味着需要DNS,一般来说域名注册商都会提供域名解析服务,当然,也有外部的一些域名解析提供商,例如著名的dnspod.   域名需要解析为一个公网的ip,互联网公司会借助这个机会来根据用户访问的ip来解析为一个更靠近用户的公网ip(这个通常需要采用商用的GSLB设备来实现),从而提升用户的访问速度,也就是很多人都听说过的CDN,不过

Java程序员也应该知道的系统知识系列之磁盘

作者:林昊 除了CPU.内存这两个最重要的也是看起来和应用性能最为相关的硬件外,磁盘也是一个非常重要的部件,尤其是IO压力比较大的存储类的系统,磁盘是一个慢速设备,所以如果使用不当,会导致应用性能受到很大的影响.   首先我们需要知道运行的机器上的磁盘的状况,可以通过执行cat /proc/scsi/scsi来查看,例如: Attached devices: Host: scsi0 Channel: 01 Id: 00 Lun: 00 Vendor: SEAGATE Model: ST33006

Java程序员也应该知道的系统知识系列之CPU

作者:林昊 去年在排查很多java应用的问题时候,看到一些现象是程序员对自己写完的程序所运行的环境了解很少,导致排查问题的时候会比较折腾,因此想到了写这个系列的文章,程序要提供功能给最终用户使用,代码只是其中的一个部分,它还需要依赖jvm.os.服务器硬件.网络.负载均衡等等来共同完成,在这个系列的文章中,将重点关注除jvm外的几个部分,更多的也只是一个科普作用,由于os我使用的都是linux,这个系列的文章中讲到的os也都默认就是linux,这是这个系列的第一篇:CPU.   Java程序在运

Java程序员也应该知道的系统知识系列之网卡

作者:林昊 对于编写分布式Java应用的同学而言,网卡的一些知识也是必须掌握的,例如什么是网卡的bonding模式,网卡的中断处理,os相关的参数等.   机器上网卡的型号可通过lspci | grep -i ether来查看,同样在看到网卡的型号后可通过google了解下网卡的状况,对于Java应用而言,主要需要关注的是网卡是否支持多队列,这个可以通过lspci -vvv | grep -i msi-x来查看网卡是否支持多队列,以及支持多少个队列,具体多队列的作用后面讲. 机器上网卡的bond

Java程序员也应该知道的系统知识系列之虚拟化

作者:林昊   如今服务器的配置越来越高端,对于Java应用而言,通常很难充分利用物理机资源,这其实并不是说Java本身没法做到用那么多的资源,而是因为通常Java是用于实现应用,应用很多的情况下对内存.cpu的需求都不会太高,这就使得我们需要有一些办法来充分提升机器的资源使用,虚拟化就是其中一种常用的方法.   虚拟化简单来说就是把一台物理机虚拟为N台机器,对于使用者而言通常会觉得虚拟机和一台单独的机器没什么区别.   对于服务器的虚拟化而言,具体关于实现虚拟化的技术像PV/FV等这些就不在这

Java程序员需要哪些AIX、Solaris知识

问题描述 在很多的招聘网站上都会看到有要求AIX.Solaris经验的:还有的要求WebLogic.WebSphere经验:有的甚至要求开发工具最好熟悉BEA的workshop:我想向前辈们问一下:对于一个Java程序员,AIX.Solaris需要了解到什么程度?我对Linux很熟练,不管是用Linux作为开发环境,还是在Linux上做Tomcat.JBoss一类的部署都没问题:但是AIX.和Solaris目录结构和管理方式都不太一样:还有WebLogic和WebSphere在什么样的场合.系统

JAVA程序员面试32问 选择自 liujun999999 的 Blog (本人做收藏)

程序|程序员 第一,谈谈final, finally, finalize的区别. 第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 第三,Static Nested Class 和 Inner Class的不同,说得越多越好(面试题有的很笼统). 第四,&和&&的区别. 第五,HashMap和Hashtable的区别. 第六,Collection 和 Collec

Java程序员面试题集(86-115)

Java程序员面试题集(86-115) 摘要:下面的内容包括Struts 2和Hibernate的常见面试题,虽然Struts 2在2013年6月曝出高危漏洞后已经显得江河日下,而Spring MVC的异军突起更加加速了Struts 2的陨落,但面试中仍然有可能被问及和此框架相关的内容,毕竟Struts 2曾经被阿里巴巴.京东以及政府企业门户网站广泛采用.另一方面,Hibernate目前仍然是ORM框架中的中坚力量,MyBatis在此领域也有不容忽视的一席之地,因此了解这两个ORM框架对Java

JAVA程序员必读:基础篇(8)

程序|程序员 JAVA程序员必读:基础篇时间:2001/09/13 13:31 作者:ZSC 太平洋网络学院 2.6什么是接口 接口是一个收集方法和常数表单的契约.当类执行一个接口,它就许诺声明在那个接口中执行所有的方法. 接口是一个设备或者一个系统,它是用于交互的无关的实体.根据这个定义,远程控制是一个在你和电视的接口:而英语是两个人之间的接口:强制在军事中的行为协议是不同等价人之间的接口.在JAVA语言中,接口是一个设备,它是用来与其它对象交互的设备.一个接口可能对一个协议是类似的.实际上,