从tomcat的永久区溢出看类加载方式以及内存分析工具

tomcat热部署导致的溢出:

tomcat设置为热部署状态 reload=‘true’可能会产生永久区的内存溢出,首先永久区存储的是类的class信息,日志报出的信息有CGLIB的报错信息,查了一下有说CGLIB的动态代理占用了大量的永久区,所以再加上tomcat reload时候旧的类信息没有办法gc就导致了永久区内存溢出。

加载方式和热部署原理

java加载方式:

java加载的方式是通过classloader对象加载,自带的classloader有sun.misc.Launcher$AppClassLoader,Bootstrap,bootstrap是由c/c++写的,在虚拟机运行时加载类,AppclassLoader是常用的classloader,通过ClassLoader.getSystemClassLoader方法获得的就是appclassloader对象,除此外还有URLClassLoader,钦定的这些classloader有一个特点叫双亲加载机制:

  1. protected Class<?> loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. synchronized (getClassLoadingLock(name)) {
  5. // First, check if the class has already been loaded
  6. Class<?> c = findLoadedClass(name);
  7. if (c == null) {
  8. long t0 = System.nanoTime();
  9. try {
  10. if (parent != null) {
  11. c = parent.loadClass(name, false);
  12. } else {
  13. c = findBootstrapClassOrNull(name);
  14. }
  15. } catch (ClassNotFoundException e) {
  16. // ClassNotFoundException thrown if class not found
  17. // from the non-null parent class loader
  18. }
  19. if (c == null) {
  20. // If still not found, then invoke findClass in order
  21. // to find the class.
  22. long t1 = System.nanoTime();
  23. c = findClass(name);
  24. // this is the defining class loader; record the stats
  25. sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
  26. sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
  27. sun.misc.PerfCounter.getFindClasses().increment();
  28. }
  29. }
  30. if (resolve) {
  31. resolveClass(c);
  32. }
  33. return c;
  34. }
  35. }

这个classloader的方法可以看出双亲委托机制:

  1. 1. 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
  2. 2. 当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
  3. 3. 当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

所以如果想实现热加载,就要摆脱双亲委托的机制,可以看到load方法并不是final所以是可以自定义的,还有一点需要注意的是类的命名空间是由全类名加类加载器所构成的,也就是说类加载器不同所加载出来的类就会钦定为不同,所以可能出现

  1. com.wks.TestReloadClass.Hot cannot be cast to com.wks.TestReloadClass.Hot

的错误,还有要实现热加载,classloader每次都要new一个新的,不然一次之后就不能再加载同一个类了,

对于已经加载了得类信息,由于jvmgc的不确定性,很不容易被gc掉,必须要所有引用都null,包括所对应的classloader,这也是tomcat永久区溢出的原因

热加载的实现:

  1. -verbose:class //vm参数,看class的加载和卸载信息

代码见附件

内存分析工具jvisualvm

jdk自带的内存分析工具,在jre/bin目录下,可以监控正在运行的java程序,查看cpu 内存,类,对象,也可以查看dmp文件,通过监控tomcat的

reload,看到了多个同一类的类信息:

参考

http://blog.csdn.net/wangyang1354/article/details/49448007

http://www.blogjava.net/heavensay/archive/2012/11/07/389685.html

null

附件列表

时间: 2024-10-07 06:04:44

从tomcat的永久区溢出看类加载方式以及内存分析工具的相关文章

堆中新生代,老年代,溢出实例,设置永久区溢出

1.堆溢出 main: public class HeapOOM { public static void main(String[] args) { List<UserBean> users = new ArrayList<UserBean>(); while (true) { users.add(new UserBean()); } } bean: public class UserBean { String name; int age; } 然后在运行的时候设置jvm参数 -

Tomcat 内存溢出对应解决方式

1.Tomcat内存溢出的原因 生产环境中Tomcat内存设置不好很容易出现内存溢出.造成内存溢出是不一样的,当然处理方式也不一样. 这里根据平时遇到的情况和相关资料进行一个总结.常见的一般会有下面三种情况: OutOfMemoryError: Java heap space OutOfMemoryError: PermGen space OutOfMemoryError: unable to create new native thread. Tomcat内存溢出解决方案 对于前两种情况,在应

方法区和运行时常量区溢出 转

方法区和运行时常量池溢出 由于运行时常量池是方法区的一部分,因此这两个区域的溢出测试就放在一起进行.前面提到JDK 1.7开始逐步“去永久代”的事情,在此就以测试代码观察一下这件事对程序的实际影响. String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象:否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用.在JDK 1.6及之前的版本中,由于常量

缓存区溢出攻击实验(3)

缓存区溢出攻击实验(3) 本实验预计分 3 个小实验来做,本文是第三个实验. 缓存区溢出攻击实验(1) 缓存区溢出攻击实验(2) 缓存区溢出攻击实验(3) 本实验跪谢大神 YSunLIN 的帮助与指导 ~ 不保证成功,一切皆有可能 ~ 背景介绍 请参照实验(1)(2). 实验目的 最终目的:只给一个需要输入正确的序列号才能验证通过的exe可执行文件,要求在不知道源代码的情况下利用缓存区溢出攻击来破解该exe文件. 注意!!! 不知道源代码的情况下 不知道源代码的情况下 不知道源代码的情况下 现实

AVA 8 :从永久区(PermGen)到元空间(Metaspace)

你注意到了吗?JDK 8早期可访问版本已经提供下载了,java 开发人员可以使用java 8 提供的新的语言和运行特性来做一些实验.其中一个特性就是完全的移除永久代(Permanent Generation (PermGen)),这从JDK 7开始Oracle就开始行动了,比如:本地化的String从JDK 7开始就被移除了永久代(Permanent Generation ).JDK 8让它最终退役了. 本文将会分享至今为至我收集的关于永久代(Permanent Generation )的替代者

JVM【第八回】:【OutOfMemoryError异常之方法区溢出】

方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等.对于这个区域的测试,基本的思路是运行时产生大量的类去填满方法区,虽然直接使用Java SE API也可以动态产生类(如反射时的GeneratedConstructorAccessor和动态代理等),在本代码中借助CGLib直接操作字节码运行时,生成大量的动态类. 这样的应用经常会出现在实际引用中:当前的很多主流框架,如Spring和Hibernate对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越

-XX:PermSize -XX:MaxPermSize 永久区参数设置

-XX:PermSize  -XX:MaxPermSize –设置永久区的初始空间和最大空间 -XX:PermSize 设置持久代(perm gen)初始值,物理内存的1/64 -XX:MaxPermSize 设置持久代最大值,物理内存的1/4 –他们表示,一个系统可以容纳多少个类型

缓存区溢出的基本原理

缓存区溢出是由于当被被溢出程序/系统调用类似于read().sprintf()\strcpy()等函数时向程序的内存空间中写入字符串,一般程序会将其放入堆或栈数据结构中 .若其对写入数据大小没有严格限制,则可能引起数据覆盖函数调用时压栈的esp栈顶指针及eip返回值,通过精心设计的写入数据,可以控制其返回值从而达到夺取控制权的效果. 由于我们的程序是以字符串的形式写入内存,所以 应避免空字符00的出现(函数会将其识别为结束字符串‘\0’,从而将我们构造的shellcode从中间截断.)但有时我们

缓存区溢出检测工具BED

缓存区溢出(Buffer Overflow)是一类常见的漏洞,广泛存在于各种操作系统和软件中.利用缓存区溢出漏洞进行攻击,会导致程序运行失败.系统崩溃.渗透测试人员利用这类漏洞,可以获取系统权限,远程执行命令等.Kali Linux提供的BED工具(Bruteforce Exploit Detector)是一款缓存区漏洞检测工具.它预置了十一种插件.这些插件针对不同的服务或系统,如FTP.SMTP.POP等.同时,这些插件内包含了已知的各种攻击载荷. BED通过加载插件,向目标主机发送攻击数据.