这个文章主要是自己关于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. Linux swappiness参数设置与内存交换

    swappiness参数设置与内存交换 by:授客 QQ:1033553122 简介 swappiness,Linux内核参数,控制换出运行时内存的相对权重.swappiness参数值可设置范围在0到 ...

  2. (python)数据结构---字典

    一.描述 由键值key-value组成的数据的集合 可变.无序的,key不可以重复 字典的键key要可hash(列表.字典.集合不可哈希),不可变的数据结构是可哈希的(字符串.元组.对象.bytes) ...

  3. return ||和return && 区别

    return a && b 如果a是true的话,返回b,否则返回a return a || b 如果a是true的话,返回a,否则返回b

  4. MyBatis笔记----SSM框架mybatis3整合springmvc spring4

    上节 无springmvc框架 http://www.cnblogs.com/tk55/p/6661786.html 结构 jar包 web.xml 与index.jsp <?xml versi ...

  5. Java常考面试题(经典)

    什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Java字节码的虚拟机进程.Java源文件被编译成能被Java虚拟机执行的字节码文件. Java被设计 ...

  6. SqlServer误删数据恢复

    误删数据,操作步骤: 第一步: 找到误删的数据库之前备份文件. 第二步: 1,修改数据库备份模式为:大容量日志 2,修改访问限制为:SINGLE_USER(单用户模式) 第三步: 执行sql一条一条执 ...

  7. c/c++ 模板与STL小例子系列<二> 模板类与友元函数

    c/c++ 模板与STL小例子系列 模板类与友元函数 比如某个类是个模板类D,有个需求是需要重载D的operator<<函数,这时就需要用到友元. 实现这样的友元需要3个必要步骤 1,在模 ...

  8. 排序算法之希尔排序的思想以及Java实现

    1 基本思想 shell排序又称之为缩小增量排序,基本思想是,先将待排序序列分割成若干个特殊的子表,分别进行插入排序,当整个表中元素"基本有序"时,再对全体记录进行一次直接插入排序 ...

  9. C# -- 抽象类与抽象方法

    C#: 抽象类与抽象方法 1.代码 class Program { static void Main(string[] args) { ; i < ; i++) { == ) { Storage ...

  10. CSS染色图标(图片)

    之前一直以为用background引入的图标无法染色(非字体图标),现在才知道有黑科技可以用,就是利用drop-shadow. 代码示例 <!DOCTYPE html> <html& ...