Java类实例化 - 内存中有什么?

Jav*_*ser 1 java instantiation classloader

我有一个基本问题.考虑这个简单的代码:

class A{
   void someMethod(){
      B b = new B(); // Line 3
      B c = new B(); // Line 4
   }
}
Run Code Online (Sandbox Code Playgroud)

当执行第3行时,将类B加载到内存中(即:我们为类型为'Class'的对象分配物理空间(假设使用id - classLaoder1.B),类型包含类B的代码).

问题1#接下来会发生什么? - B类(占B的状态)的实例被创建基于该classLoader.B实际上包含B的信息的事实(已分配的物理内存)?

问题2#另外,在第-4行,由于classLoader.B存在于内存中,因此在内存中创建了一个包含c状态的对象?

Rom*_*ner 7

那么,你的例子和描述有点模糊,以简短的方式回答你的问题.

您指的是不同的类加载器,但是在加载哪个类时您没有包含任何示例代码.在目前的形式中,代码甚至不会编译,因为缺少返回值 - 但是让我们继续你的问题.

堆是JVM在启动时创建的内存区域,可以在运行时动态增加和减少.它分为不同的部分.YoungGen将保存短命对象,OldGen将保存在YoungGen空间中存活的对象的对象状态,最后是PermGen空间,因为它的名称建议应该包含永久类元数据和描述符.因此,PermGen空间保留用于与类(如静态成员)绑定的类和东西,如果您处理提供某种热部署功能的应用程序服务器或插件机制,则必须处理它.(更准确一点,在Sun的JVM中,PermGen空间实际上是内存的一个独立部分,并不真正属于堆,但是不同的JVM供应商因此可能有不同的定义)

在此输入图像描述 参考:SAP JVM的配置和设置

调用时可能发生两种情况someMethod():

  • 在应用程序启动时,应用程序类加载器已经加载了B.
  • B包含在由子类加载器加载的类中

在第一种情况下,类定义的内存在启动时在堆的PermGen空间内分配,并且仅在应用程序关闭时释放.在后一种情况下,还有存储在堆的PermGen空间中的类的内存,但是在调用loadClass(...)应该加载类的类加载器时.这里,如果没有强引用指向该类加载器加载的任何类,则可以释放内存.通常,枚举或单例类(它们拥有对自身的强引用)将阻止正确卸载那些加载的字节并因此产生内存泄漏.

如果你实现了其中一个应用程序框架并对其进行调试,那么你将会看到究竟发生了什么.要通过类加载器加载一个类,loadClass(...)调用该方法首先检查它是否已经加载了该类,然后询问他的父母是否知道这个类(它还检查她是否已加载该类或其父类,... ).只有当之前没有加载类(通过这个类加载器或任何父类)时,当前(子)类加载器将执行findClass(...)哪个进一步调用defineClass()哪个实际上将字节从某个输入文件或流转换为Class表示.该Class对象包含蓝图(方法的签名,包括参数的数量和类型,返回值和抛出的异常).在尝试加载类时,通常扩展类以及定义的接口也会被加载(如果在类加载器树中尚未知道) - 但是包含成员的类型尚未加载!当要实例化类时,它们将被加载.

在此输入图像描述 参考:ClassLoader如何在Java中工作

在创建新实例时,new操作员在newInstance(...)内部调用该方法并为该实例的所有成员保留内存.因此,如果当前类加载器或其父类尚未知该成员的类型,则在分配任何值之前将加载它.然后,执行类的构造函数(根据使用new操作调用的构造函数),并将值分配给堆上变量占用的内存(通常在Eden空间中).在内存中构造对象后,new运算符将返回对象的引用,并且该对象已准备好在代码中使用.

c在您的示例中实例化方式与b- 首先类加载器必须检查类B是否已加载.正如它B之前加载的那样,它只是B从其本地缓存中获取并因此返回该类.接下来,在newInstance(...)类上执行该方法以实例化新对象.因此,再次在堆上分配成员变量的内存 - 在初始检查之后是否已经加载了所需的类 - 执行构造函数并返回对新创建和初始化对象的引用.

如果您的类具有静态方法或静态成员,则它们将被分配到PermGen空间,因为它们属于该类并且在所有实例之间共享.

有一点需要注意:如果c应该由peer或peer的子类加载器(CL2)加载并且b是由姐妹类加载器(CL1)定义的(因此没有父实际上已经定义了类),对等类加载器CL2将加载(并定义)它自己的版本B似乎与姐妹的加载器CL1的版本相同,但它们实际上是Java的不同类,因为加载该类的类加载器实际上是类的一部分.这意味着CL1- B!= CL2- B尽管两个版本共享相同的方法和字段.铸造cbB将导致ClassCastException因此.

为了完整起见,尽管你没有要求这样做,但在调用方法时会发生不同类型的内存分配.传递的变量被压入堆栈,每个线程都有自己的实例,如果方法返回,则从堆栈中弹出(包括返回值).此外,每个块({和之间的部分})创建一个新的堆栈帧(这就是块内声明的变量对块外部的区域不可见),其中存储了该块的局部变量.更多信息请点击此处

在此输入图像描述 参考:了解堆栈和堆教程