这个文章主要是自己关于jvm内存的一点思考,范围比较杂,设计类加载器,方法区和内存泄露等

一、 内存是怎么分配的

主要是指针碰撞和空闲列表两类。新生代一般是复制算法,老年代一般是标记整理(cms用了标记清除导致内存碎片较多)。复制和标记整理采用指针碰撞,标记清除采用标记清除。如果是指针碰撞需要考虑指针的冲突,有cas和本地线程分配缓冲两种策略。

二、 方法区

方法区包括 常量池(jdk7被移入堆中),代码,类信息,类静态变量。jdk8后方法区实现为本地内存,受本机物理内存影响。

三、 java对象的生命周期

创建对象(如果对象的类型没有加载则加载),使用对象,不可达,被标记,如果实现了finalize进入终结队列,回收

四、 Class对象是在方法区还是堆中

深入理解java虚拟机p215,Class是在方法区中,作为程序访问方法区的入口。这一点其实没有太大的意义,但是知道类的静态字段存在在方法区中,在类加载的准备阶段初始化内存是很重要的。

五、java对象的大小

对象由对象头,数据和对其对齐填充字节。主要考虑的是对象头,对象头包含一个指向对应Class的指针和对象信息。32位下,两者都是4字节,共8字节。64位下两者都是8字节共16字节,如果开启了指针压缩那么Class指针为4字节共12字节。如果是数组则多4个字节表明数组的长度。

六、 类加载的初始化阶段

在java并发编程思想中有个代码例子如下:

public abstract class testAbstract {
public testAbstract() {
System.out.println("absStart");
testChildImpl();
System.out.println("absEnd");
} public abstract void testChildImpl();
}
public class testAbstractImpl extends testAbstract {
private String str = "123"; public testAbstractImpl() {
System.out.println(str);
System.out.println("real");
} public static void main(String[] args) {
new testAbstractImpl();
} @Override
public void testChildImpl() {
System.out.println(str); }
}

问输出的是什么? 答案:

absStart
null
absEnd
123
real

以前在看这个问题的时候根本不知道答案,看了也觉得输出中的null非常难以理解,在重新看书的时候想到了其实就是关于类的初始化。上面的子类作为启动类需要初始化,但是根据加载的顺序,父类要先初始化,父类初始化的时候调用了子类的函数,这时子类输出静态字段,因为子类的静态字段在类加载的准备阶段已经分配了内存并且有默认值null,“123”要到子类初始化的时候才去赋值,所以输出为null。一切都解释通了,这也是我喜欢计算机的原因,一切都有原因。 (可以参考深入理解jvm的p219)

七、Class.forName和Classloader

区别在于Class.forName加载类的时候会经过加载阶段的初始化阶段。Classloader的loadClass根据传入resolve是否为true执行连接阶段(验证,准备,解析),始终不会执行初始化阶段。测试代码如下:

  public class ClassLoadTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<? extends ClassLoader> aClass = ClassLoader.getSystemClassLoader().getClass();
Method loadClass = aClass.getDeclaredMethod("loadClass", String.class, boolean.class);
loadClass.setAccessible(true);
loadClass.invoke(systemClassLoader, "jvm.classLoad.test.ClassOut", true);
System.in.read();
// Class.forName("jvm.classLoad.test.ClassOut");
}
}

另外补充下类加载器的一些重要方法:

  1. loadClass 定义了双亲委派模型的框架
  2. findClass 自己基本ClassLoader一般重写这个方法
  3. findLoadedClass 找到当前类加载器加载的类
  4. defineClass 类加载的加载阶段(注意这个时候Class对象已经在内存中生成)
  5. resolveClass 类加载的验证、准备、解析阶段

八、 类加载器的回收

很多场景都是讲的是类的回收,我在网上查的时候很少有谈到类加载的回收。类加载的回收三个条件都有讲到,其中需要类加载器被回收,那么类加载器什么时候能被回收呢?在这里的我的理解是当类加载器不可达时即可被回收(代码中就是将所有类加载器的引用消除,一般我们都在本地存有它的引用然后置为null,但是有很多意想不到的类加载器引用泄露的问题,例如序列化,log4j,ThreadLocal问题),注意类的回收是方法区,类加载器则是堆中。由此可以解释ThreadLocal导致tomcat内存泄露的问题(tomcat新版已经解决了这个问题https://wiki.apache.org/tomcat/MemoryLeakProtection)。对于这个问题有时间在Tomcat好好看下源码。网上看了很多关于类加载器泄露的坑,自己编写要十分谨慎。

九、 内存泄露

看了一篇文章讲了内存泄露很不错,自己在公司遇到过一次连接泄露的问题,所以正好整理了下相关的问题来分享下。

场景描述:我们公司的用户服务对接了第三方腾讯云通信服务,在用户注册的时候我们需要走http接口调腾讯云,问题就出在http连接那块,同事当时采用了,线上出现了cpu100%的问题,日志出现java.lang.OutOfMemoryError: GC overhead limit exceeded

排查思路:这个其实很好定位,本来还想打印线程栈看下到底是哪个导致的cpu100%,一看日志直接定位到gc出问题。GC overhead limit exceeded是指gc占用了大量的cpu时间又回收不了内存引起的,从内存泄露去考虑,重启服务 ,启动参数加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./user.hprof -verbose:gc -Xloggc:user%t.log。问题复现的时候获得了堆的dump文件,然后通过Jprofile分析,发现有大量的http.HttpKeepAliveCache实例,占用了80%的内存,大致定位到是由于http连接泄露。同事封装的HttpUtil中使用了HttpsURLConnection,在读取数据的时候没有关闭InputStream导致连接没有关闭。

十、String拼接导致内存溢出

公司的后台有段时间会间歇性的卡顿,严重的情况下会导致cpu100%。在cpu100%的时候,通过top定位到进程号,然后输入H切换到线程,记住具体的进程号,使用jstack打印java进程的线程栈,jstack输出为十六进制,需要将top的转换成十六进制的然后入找线程经常卡在哪个方法。定位到方法发现是查询用户关联设备号的方法出问题,方法的逻辑是从数据库查询设备号,在内存中以以逗号分隔拼接返回,如1,2,3。这个bug的原因是有如下:

  1. sql出错,导致查询返回数据量很多,正常情况最多几百个,但是异常情况有七万个设备号
  2. 字符串拼接采用str+="1234"的形式,导致大量的内存分配和回收。

运营在点击后台查询的时候发现没返回,点掉就重新点,导致服务器多个线程卡在这个方法造成cpu100%。解决完sql,改用StringBuilder问题解决。

JVM内存管理的一些思考的更多相关文章

  1. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  2. java jvm内存管理/gc策略/参数设置

    1. JVM内存管理:深入垃圾收集器与内存分配策略 http://www.iteye.com/topic/802638 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想 ...

  3. JVM内存管理------垃圾搜集器参数精解

    本文是GC相关的最后一篇,这次LZ只是罗列一下hotspot JVM中垃圾搜集器相关的重点参数,以及各个参数的解释.废话不多说,这就开始. 垃圾搜集器文章传送门 JVM内存管理------JAVA语言 ...

  4. Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收

    很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...

  5. JVM内存管理(二)

    JVM内存管理          JVM在执行java程序的过程中,会把内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖 ...

  6. JVM内存管理及垃圾回收

    一.JVM内存的构 Java虚拟机会将内存分为几个不同的管理区,这些区域各自有各自的用途,根据不同的特点,承担不同的任务以及在垃圾回收时运用不同的算法.总体分为下面几个部分: 程序计数器(Progra ...

  7. 现代JVM内存管理方法的发展历程,GC的实现及相关设计概述(转)

    JVM区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...

  8. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  9. Java的内存 -JVM 内存管理

    一.综述 如果你学过C或者C++,那么你应该感受过它们对内存那种强大的掌控力.但是强大的能力往往需要更强大的控制力才能保证能力不被滥用,如果滥用C/C++的内存管理那么很容易出现指针满天飞的情况,不出 ...

随机推荐

  1. spring学习总结——装配Bean学习一(自动装配)

    一.Spring配置的可选方案 Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系.但是,作为开发人员,你需要告诉Spring要创建哪些bean并且如何将其装配在一起.当描 ...

  2. Linux 无线网卡配置

    无线网卡常见的配置选项 某TL-WR842N路由器无线配置选项含义: 无线名称 路由器的无线(Wi-Fi)名称.无线密码 无线加密使用WPA2-PSK/WPA-PSK加密方式.AES加密算法,无线密码 ...

  3. [转载]Windows 2003 R2 SP2 VOL 企业版(简体中文)

    Windows 2003 R2 SP2 VOL 企业版(简体中文) 要是这个的话,分享个电驴的下载连接吧(可以复制后用快车和迅雷直接下)32位版CD1:SHA1值:d0dd2782e9387328eb ...

  4. [20190213]测试服务端打开那些端口.txt

    [20190213]测试服务端打开那些端口.txt --//前几天测试使用发送信息到/dev/tcp/ip_address/port,测试端口是否打开.写简单写一个脚本验证看看. $ seq 1 65 ...

  5. Django之--网页展示Hello World!

    上一篇:Django的安装启动完毕后,本文来试下hello world的效果~ 好吧,又开始了喜闻乐见的Hello World环节,本文使用Linux环境演示(Windows太麻烦). [root@p ...

  6. linux系统/var/log目录下的信息详解

    一./var目录 /var 所有服务的登录的文件或错误信息文件(LOG FILES)都在/var/log下,此外,一些数据库如MySQL则在/var/lib下,还有,用户未读的邮件的默认存放地点为/v ...

  7. iOS图片存在本地、再从本地获取图片

    图片存在本地.再从本地获取图片 //将图片保存到本地 + (void)SaveImageToLocal:(UIImage*)image Keys:(NSString*)key {     NSUser ...

  8. iOS 指纹解锁 验证TouchID

    iOS指纹解锁 1.首先,引入依赖框架 LocalAuthentication.framework #import <LocalAuthentication/LocalAuthenticatio ...

  9. wamp 中安装cakephp Fatal error: You must enable the intl extension to use CakePHP. in XXX

    今天在wamp下安装cakephp3.x的时候,报出这么一条错误:Fatal error: You must enable the intl extension to use CakePHP. in ...

  10. LeetCode算法题-Sum of Left Leaves(Java实现)

    这是悦乐书的第217次更新,第230篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第85题(顺位题号是404).找到给定二叉树中所有左叶的总和.例如: 二叉树中有两个左叶 ...