Java对象与内存分配

##Java对象与内存分配
Java中一切都是对象,对象是Java运行的单元,知道对象是如何存在的、什么时候是可见的,才知道怎样运用对象来完成相应的操作。
Java运行时对象和变量等都是在内存中,可以根据内存中的数据来判断这些对象的可见性。下面了解一下Java对象在内存中的分配。
内存主要分为:程序计数器、虚拟机栈、堆、方法区、本地方法栈。
程序计数器可以看作是当前线程所执行的字节码的行号指示器。它是线程私有的。
虚拟机栈也是线程私有的,描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
本地方法栈同虚拟机栈非常相似,,为虚拟机使用到的Native方法服务。也是线程私有的。
Java堆是所有线程共享的,是Java虚拟机所管理的内存中最大的一块,在虚拟机启动时创建。几乎所有的对象实例以及数组都要在堆上分配内存。Java堆是垃圾收集器管理的主要区域,因此也称作“GC堆”。Java堆在计算机实际内存中可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
C语言可以直接操控电脑的内存,所以C语言在操作数据时要手动分配内存给数据,在数据用完后要及时释放内存,以免发生内存溢出问题。Java是用C语言写的,它对内存的分配和回收机制已经处理了,所以不需要程序员再去手动的处理内存的申请与释放操作。
Java虽然有回收机制(GC),也不能肆无忌惮的创建对象。建的多了GC频繁的回收也会影响效率,也会导致内存溢出。了解Java对象的内存分配有助于服务性能的提高。

Java变量分为局部变量和成员变量。局部变量主要是:a、形参,即方法的参数;b、方法中的变量;c、代码块中的变量。成员变量主要是非静态变量即实例变量和静态变量即类变量。
变量的定义和初始化的内存分配的时机是有先后顺序的,如下:
a、类变量和静态代码块为同一级,优先分配内存,其次是实例变量和代码块这一级别,最后是为构造器初始化分配内存;
b、Java要求定义处在内存分配中同一级别的变量时,必须采用合法的前向引导。通俗的讲就是类变量和静态代码块定义和初始化的变量要有先后顺序,实例变量和代码块定义和初始化要有先后顺序;
c、内存分配中同一级别的变量,先按顺序定义,都定义好后再按顺序初始化。
下面举例说明:

public class MemoryTest {     //第一步
    int surplus = num - count;          //a //第六步
    static int count = 0;     //第二步
    int max;  //第七步
    {  //第八步
        System.out.println("代码块初始化前surplus=" + surplus);
        count = 2;
        surplus = num - count;
        persons = 1;                  //b
        int var = 3;
        System.out.println("代码块初始化后surplus=" + surplus);
    }
    static int num = 5;     //第三步
    int persons = 2; //第九步
    {//第十步
        max = count -persons + 1;     //c
    }

    public static void main(String[] args){  //第四步
        MemoryTest memory = new MemoryTest();  //第五步
        System.out.println(memory.persons);
    }
}

执行结果是

代码块初始化前surplus=5
代码块初始化后surplus=3
2

这个例子很好的说明了变量的内存分配和分配时机。这里的变量存在实例变量、类变量、代码块中的局部变量。这里对代码分析下内存分配和分配时机.
在第六步实例变量surplus初始化时用到了类变量num和count,num和count的定义和初始化是在后面代码,之所以实例变量可以操作类变量num和count是因为类变量是属于类本身的,就是在加载MemoryTest类时就对类变量进行定义和初始化.
类初始化时先将count和num定义到栈内存中,并分配内存空间,此时两个变量的默认值都是0.再根据初始化代码进行初始化.如图:
这里写图片描述
在第五步创建类MemoryTest对象new MemoryTest()时会在堆栈中为实例memory分配内存并赋默认值null,再在堆内存中创建对象。下面用图来分析MemoryTest对象的创建过程。
首先,在堆内存中分配空间,将对象的实例变量逐个定义,类型为int型,默认值都是0,如图:
这里写图片描述
其次,根据代码和变量的先后顺序为变量初始化,实例变量和非静态代码块按顺序执行,如第六步,
这里写图片描述
在执行第八步代码块时,会对persons赋值,虽然定义persons的代码是在代码块后,但对象定义变量是在初始化和代码块之前执行,所以在代码块中为persons赋值不会报错。
这里写图片描述
第八步代码块中定义的局部变量var的作用域仅是代码块,代码块结束后var也随记被销毁,空间被回收。
这里写图片描述
这里需要注意一个问题,在第八步代码块中,persons只能被赋值,不能作为变量用于其他语句的计算或引用等操作,原因是persons是在第九步定义被赋值,代码现在执行在第八步,我们说实例变量和代码块是顺序执行的,还未执行到第九步,也就是persons还未结束定义初始化,所以不能操作persons变量,但在第九步初始化结束,persons变量就可以使用了。
这里写图片描述
MemoryTest对象创建完之后memory指向对象地址,调用对象的persons实例变量。
这里写图片描述
main方法执行完之后,堆栈中的memory将被销毁,空间回收,此时MemoryTest对象将没有实例指向它,它也会被GC回收。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页