引入

方法区是用于存放一些元信息的, 如类Class信息, 以及一些运行时常量池, 类Class信息被存放在元空间中
(Metaspace), 由此可见, 当我们的类Class信息达到了一定程度后会导致元空间的内存溢出, 同时也是方法区
的内存溢出

元空间: 由JDK1.8才开始使用, 同时废弃了永久代, 元空间使用的是操作系统的直接内存, 并且由元空间虚拟机
       独立管理, 在一般情况下是不会发生内存溢出的, 当类加载的越来越多时, 元空间的大小也会动态的扩展,
       当达到了一个局限值后, 就会触发垃圾回收器来回收不再使用的Class对象, 一般元空间的大小默认为21M

为了能够造成元空间内存溢出, 我们需要设置一定的参数来固定元空间的大小, 即-XX:MaxMetaspaceSize=20m,
设置了20m的元空间的大小后, 接下来我们将不停的加载不同的类, 从而使得撑满元空间, 在这里我采用的是通过
自定义classloader的方式来加载类信息, 并且会用到命名空间, 如果对命名空间不熟悉的可以看下上面的关于类
加载器的相关文章, 在那里我对类的命名空间这个概念进行了详细的讲解与演示, 并且这个类加载器的代码我就
不贴出来了, 上面同样有一篇文章专门写了如何自定义类加载器

在往上有看到用CGlib方式实现的, 由于目前阶段我并没接触到这个(貌似是一个类似于动态代理一样可以动态
的在运行期生成类的东西), 所以我采用了自定义类加载器的方式来实现
  • 测试代码

    class TestClass4_ {}
    public class TestClass4 {
      public static void main (String[] args) throws Exception {
          ArrayList<Class> classlist = new ArrayList<>();
          while ( true ) {
              TestClass4Loader loader = new TestClass4Loader( ClassLoader.getSystemClassLoader().getParent() );
              loader.setAddress( "C:/Users/zhongshenglong/Desktop/Notes/JVMStudy/target/classes/" );
              Class<?> c = loader.loadClass( "com.fightzhong.jvm.memory.TestClass4_" );
              classlist.add( c );
          }
      }
    }
    
  • 分析

    注意: 上面的类加载器的代码是我临时写出来的, 可能跟上面的文章上的命名不一样, 但是实现是相同的
    首先我创建了一个ArrayList, 目的是为了将下面创建的Class对象防止被垃圾回收器因为没有引用而导致回收,
    从而使得Class对象的元信息能够长久的放置在元空间中, 然后我显示的设置了类加载器的加载路径为我项目的地
    址, 即AppClassloader的加载路径, 这里需要注意的是, 由于命名空间的存在, 我创建的loader对象不是单例
    的, 所以!!!在循环中创建的每一个类TestClass4_的Class对象都是不同的!!!, 从而使得元空间中存在
    大量相同的Class对象, 这个接下来我会用图片给大家看清楚, 之后运行程序, 当运行了一会之后, 即报出了异
    常
    

results matching ""

    No results matching ""