zoukankan      html  css  js  c++  java
  • Java学习1:图解Java内存分析详解(实例)


    首先需要明白以下几点:

    1. 栈空间(stack),连续的存储空间,遵循后进先出的原则,用于存放局部变量
    2. 堆空间(heap),不连续的空间,用于存放new出的对象,或者说是类的实例。
    3. 方法区(method),方法区在堆空间内,用于存放①类的代码信息;②静态变量和方法;③常量池(字符串敞亮等,具有共享机制)。
    4. Java中除了基本数据类型,其他的均是引用类型,包括类、数组等等。
    5. 数据类型的默认值
      基本数据类型默认值:
      数值型:0
      浮点型:0.0
      布尔型:false
      字符型:u0000
      引用类型:null
    6. 变量初始化
      成员变量可不初始化,系统会自动初始化;
      局部变量必须由程序员显式初始化,系统不会自动初始化。

    实例进行分析。

    创建类

    分别是Student、Computer、Test,代码如下:

    public class Student {
    
    	int score;
    	int age;
    	String name;
    
    	Computer computer;
    
    	public void study() {
    
    		System.out.println("studying...");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    public class Computer {
    	int price;
    	String brand;
    }
    
    • 1
    • 2
    • 3
    • 4
    public class Test {
    
    	public static void main(String[] args) {
    
    		Student stu = new Student();
    
    		stu.name = "xiaoming";
    
    		stu.age = 10;
    
    		stu.study();
    
    		Computer c = new Computer();
    		c.brand = "Hasse";
    
    		System.out.println(c.brand);
    
    		stu.computer = c;
    		System.out.println(stu.computer.brand);
    
    //		System.out.println("----------------------------------------");
    //
    //		c.brand = "Dell";
    //
    //		System.out.println(c.brand);
    //		System.out.println(stu.computer.brand);
    //
    //		System.out.println(stu.computer.brand == c.brand);
    
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    代码分析

    我们知道,程序的入口是main(),因而从main方法从上到下、从左到右进行分析。

    Student stu = new Student();

    ①首先,Java虚拟机(JVM)去方法区寻找是否有Test类的代码信息,如果存在,直接调用。如果没有,通过类加载器(ClassLoader)把.class字节码加载到内存中,并把静态变量和方法、常量池加载(“xiaoming”、“Hasse”)。
    ②走到Student,以同样的逻辑对Student类进行加载;静态成员;常量池(“studying”)。
    ③走到stu,stu在main方法内部,因而是局部变量,存放在栈空间中。
    ④走到new Student,new出的对象(实例),存放在堆空间中,以方法区的类信息为模板创建实例。
    ⑤‘’=‘’赋值操作,把new Student的地址告诉stu变量,stu通过四字节的地址(十六进制),引用该实例。
    如下图:
    这里写图片描述

    stu.name = “xiaoming”;

    ⑥stu通过引用new Student实例的name属性,该name属性通过地址指向常量池的"xiaoming"敞亮。

    stu.age = 10;

    ⑦s实例的age属性是基本数据类型,基本数据类型直接赋值。

    stu.study();

    ⑧调用实例的方法时,并不会在实例对象中生成一个新的方法,而是通过地址指向方法区中类信息的方法。

    ⑥⑦⑧的过程如下图:
    这里写图片描述

    Computer c = new Computer();

    同stu变量的生成过程。

    c.brand = “Hasse”;

    同stu.name = "xiaoming"过程。

    stu.computer = c;

    ⑨把c对象对Computer实例的引用赋值给Student实例的computer属性。亦即:该Student实例的computer属性指向该Computer类的实例。
    如下图:
    这里写图片描述


    拓展

    改变brand的地址指向。

    为进一步理解,我们把注释内容去掉:
    ⑨重新将Computer实例的brand属性指向"Dell"常量,那stu.computer.brand指向谁呢?Dell还是Hasse?

    c.brand = "Dell";
    
    • 1

    根据刚才的分析可知:
    stu通过地址引用Student实例,而该实例的computer的指向和c的指向是同一个Computer实例,因而改变该Computer实例的brand属性的指向,两者都会改变。
    举个例子:
    访问大明,和访问大明的儿子的爸爸,实质上访问的是同一个对象:大明。
    因而,最终的结果是true。

    理解字符串常量及常量池

    下面我们添加新的代码,如下:

    		String str = "Dell";
    		System.out.println(c.brand == str);
    
    • 1
    • 2

    结果会如何呢?
    根据常量池具有共享性,可知并不会生成新的常量"Dell",而是会把str通过地址指向原来的"Dell",因而结果是true。

  • 相关阅读:
    在包a中新建一个类A,在类A中有一个int add(int m)方法,用来求1+2+…+m 的和。在包b中新建一个类B,在类B中有一个int cheng(int n)方法,用来求n! 的结果。在包c中新建一个主类C,调用A、B中的方法输出1+2+…+30的和, 以及5!的计算结果。
    在包a中编写一个类Father,具有属性:年龄(私有)、姓名(公有); 具有功能:工作(公有)、开车(公有)。 在包a中编写一个子类Son,具有属性:年龄(受保护的)、姓名; 具有功能:玩(私有)、学习(公有)。 最后在包b中编写主类Test,在主类的main方法中测试类Father与类Son。
    简单且线程安全的两个单例模式java程序
    Condition的优点
    Java设计模式—生产者消费者模式(阻塞队列实现)
    Java NIO使用及原理分析 (一)
    一步步优化JVM五:优化延迟或者响应时间(1)
    UML 类图
    Eclipse中构建Fluent风格到Formatter
    Memcache 问题集锦
  • 原文地址:https://www.cnblogs.com/newcityboy/p/11216349.html
Copyright © 2011-2022 走看看