一个简单的java的内存分析

一、什么是内存

        关于java的内存解释每个人都有自己的解释,网上也看了一大堆,云里雾里。

我个人对java内存的理解:简化的理解为就是存放数据的区域。因为java有虚拟机的存在,它自动会从电脑的内存中分配一定的空间,暂时不需要我们考虑这些。

二、java中的内存中的模块分法

        对于内存中的模块分法,目前好多种分法,3块的分法(栈、堆、方法区),4块分法(代码区、栈、堆、静态区),5块、甚至7块区域的都有。这些分法仁者见仁智者见智了,我自己倾向于3块区域的划分。


        3块区域的划分:(概念解释)

1、栈:就是一种线性表

特点:先进后出,后进先出【指的是数据的存放读取顺序】。记得大学时老师把栈比喻成:枪的弹夹,满梭子的子弹夹,最先放进去的,肯定是最后的一个被打出去。

栈中存放的内容:局部变量、形参。

优点:存取速度快;缺点:栈中存放的数据生命周期是确定的(即:只在一个方法内有效)。

2、堆:就是一种线性表(一种树形的数据结构,)

特点:先进先出。比喻:一辆火车穿越山洞,火车头先进入山洞,同时也是火车头先出山洞。

堆中存放的内容:new出来的东西(例如:Student s=new Student();)。

3、方法区:

存放内容:方法的字节码、和常量池。

 

三、内存中的数据存储细节具体分析

简单案例1 :先用java代码引入这个问题:

public class Student {

String name;  //姓名

int age;      //年龄

double hight;  //身高

public Student(){}  //无参构造函数

public Student(String name,int age,double hight)   //有参构造函数

{

this.name=name;

this.age=age;

this.hight=hight;

}

public static void main(String[] args)

{

int salary=199;// 工资   这个是局部变量

Student stu=new Student(“刘诗雯”,25,175);

System.out.println(“暂时不打印数据,此行代码”);

 

}

}

内存如下图:

 

 

解释

关于对0x990x88代表的内存中的16位地址。或者就叫地址,粗暴的理解如同家庭住址一样。

具体分析:

1. 程序执行的入口是main()方法,所以在main()方法中的代码第一行是int salary=199;

salary 在方法体内部,所以是局部变量【关于局部变量、成员变量、类变量也叫静态变量的我个人理解,会在文章最后写出来】

按语法局部变量存放在栈中,所以在图中的栈中有一个salary 变量名,而且它的生命周期也是确定的(main方法结束,局部变量的salary变在内存中消失。此时第一行代码执行完成)。

2. 执行第二行代码

 Student stu=new Student(String name,int age,double hight);

等号运算符的执行顺序是从右到左。

所以  执行new Student(String name,int age,double hight);

因此在方法块中分出一块是编译后的new Student(String name,int age,double hight)字节码信息。其中关键字new 按照定义在堆中开辟一块空间(0x99),包括name agehight属性信息。

又因name String类型。

String jdk源码修饰的是public final class String

final关键字修饰的数据类型是常量。所以String name=刘诗雯是常量。因此在方法块的常量池中以存放一个  刘诗雯,--》地址为(0x88)  。它指向堆中name【此时name在堆中存放的是指向常量池中刘诗雯的地址(0x88)】

agehigth 属性是基本数据类型修饰,所以在new 方法中存放的是内容。age=25,hight=175.0;(图中画的时候有瑕疵,double的默认值是0.0)

右边的代码分析结束,将左边赋值给右边。右边是String stu stuString引用类型的变量,变量是放在栈中的。在栈中有一个变量名stu,引用变量名在栈中存放的不是内容,而是指向的一个地址(0x99)

第二行的代码运行结束,这个简单的内存就分析结束了。

案例二:

public class Demo {

    public static void main(String[] args) {

Demo t=new Demo();

int age=40;

    Person tom=new Person(1,20,"海淀");

Person jack=new Person(2,30,"朝阳");

t.method0(age);

t.method1(tom);

t.change2(jack);

System.out.println(age);

System.out.println("id:"+tom.id+",age:"+tom.age+",school:"+tom.school);

System.out.println("id:"+jack.id+",age:"+jack.age+",school:"+jack.school);

}

    public void method0(int i){

     i=3366;

    }

    public void method1(Person p0){

    

        p0=new Person(3,22,"西城");

    }

    public void change2(Person p1){

        p1.setAge(66);

    }

}

 

class Person{

int id;

int age;

String school;

public Person (int a,int b,String c){

id=a;

age=b;

school=c;

}

 

public void setAge(int a){

age=a;

}

}

 

内存分析图

内存具体分析:

1.从程序的入口main开始分析:

代码执行的第一行:

Demo t=new Demo();

通过关键字new 在堆中开辟一个空间,指向方法块中的newDemo()的字节码。

Dem是一个类,类是引用数据类型,所有它在栈中创建一个变量名t存放指向堆中的new出来的地址0x77,堆中的0x77指向方法块中的new Demo()字节码。

2.代码执行第二行:

int age=40;

创建局部变量age ,并初始化赋值为40,在栈空间中。

3. 代码执行第三行

Person tom=new Person(1,20,"海淀");

右边通过new在堆中开辟一块空间存放idageschool 属性信息。地址为0x99,并为属性初始化赋值,id=1,age=20,school=”海淀”

school字段是String类型,在方法块的常量池中创建一个 ”海淀” ,school中存放为常量池中”海淀”的地址值。

左侧通过类Person 创建一个栈中变量名为tom对象,tom中存放0x99地址。

4.代码执行第四行、

Person jack=new Person(2,30,"朝阳");

解释同上。

5.代码执行第5行。

t.method0(age);

将变量age=40 的值传给方法method0(int i);中形参i,此时形参在栈中创建一个变量名为int 类型的ii在栈中的值为40

public void method0(int i){

     i=3366;

    }

method0方法体内又进行一次,将3366赋值给i,此时i值被修改为3366

因为i为局部变量,局部变量的有效性范围在方法体中,所以当方法method0方法运行结束,i变失效,在栈中的被释放。而age依然为40。因为变量age的有效范围在main方法中。同理当main方法运行完,age也在栈中会失效,

6.代码执行第六行

t.method1(tom);

通过类的对象调用method1()方法

public void method1(Person p0){

        p0=new Person(3,22,"西城");

    }

形参为Person p0

根据概念形参在栈中,栈中创建一个变量名为p0的变量,p0的类型为类,所以在p0中存放是一个地址,将Person 类型的tom 对象传给p0,因此p0存放的是tom相同指向的地址0x99。然后执行方法体内的语句,等号左边又进行一次new关键字通过newint id,int age,String school)字节码信息匹配创建一个堆空间0x66p0存放的地址变更为0x66,并为其属性赋值,id=3age=22school存放一个指向常量池中”西城”的地址。到此方法method1执行结束。而方法中所有的局部变量在栈中失效被释放。所以p0失效,堆中开辟的空间0x66因缺少指向,被java 虚拟机视为垃圾,被自动回收(java中垃圾回收只回收堆中的垃圾)。

方法块的常量池中 ”西城” 依然存在。

7.代码执行第七行

t.change2(jack);

解释同代码执行第六行。不同点是,方法体的语句是

p1.setAge(66);

p1也是引用类型存放也是jack传递过来的地址0x88。通过setAge法修改0x88地中的age属性值,此时0x88age不在等于30,被修改为66了。

方法运行结束,栈中的局部变量名p1失效。

8、至此内存分析结束。

System.out.println(age);

System.out.println("id:"+tom.id+",age:"+tom.age+",school:"+tom.school);

System.out.println("id:"+jack.id+",age:"+jack.age+",school:"+jack.school);

通过上面的输出语句,然后在内存中找到其对应的值。

最后输出结果为

40

id:  1  age:  20  school:海淀

id:  2  age:  66  school:朝阳

 

【关于局部变量 成员变量  静态变量解读】

局部变量:定义在方法体内部的变量,有效范围,仅在方法体内有效

成员变量:定义在类中的变量,伴随着类的有效期

静态变量;也成为类变量  有个关键字修饰 static 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值