zoukankan      html  css  js  c++  java
  • java内存管理实例讲解

    一.java虚拟机运行时内存分配图

    二.栈 堆 方法区简介

    1.栈

      1. 每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)

      2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)

      3. 栈属于线程私有,不能实现线程间的共享!

      4. 栈的存储特性是“先进后出,后进先出”

      5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!

    2. 堆

      1. 堆用于存储创建好的对象和数组(数组也是对象)

      2. JVM只有一个堆,被所有线程共享

      3. 堆是一个不连续的内存空间,分配灵活,速度慢!

    3.方法区

      1. JVM只有一个方法区,被所有线程共享!
      3. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等)

    三.实例讲解

    1.代码

    class Point {
        private double x;
        private double y;
        Point(double x1, double y1) {
            x = x1;
            y = y1;
        }
        public double getX() { return x; }
        public double getY() { return y; }
        public void setX(double i) { x = i; }
        public void setY(double i) { y = i; }
    }
    class Circle {
        private Point o;
        private double radius;
        Circle(Point p, double r) {
            o = p;
            radius = r;
        }
        public void setO(double x, double y) {
            o.setX(x);
            o.setY(y);
        }
        public Point getO() { return o; }
        public double getRadius() { return radius; }
        public void setRadius(double r) { radius = r; }
        public double area() { return 3.14 * radius * radius; }
        Circle increaseRadius() {
            radius++;
            return this; 
        }
    }
    public class TestCircle {
        public static void main(String args[]) {
            Circle c1 = new Circle(new Point(1.0, 2.0), 2.0);
            System.out.println("c1:" + c1.getO().getX());
            c1.setO(5, 6);
            System.out.println(c1.increaseRadius().increaseRadius().area());
        }
    }
    

    2. 构造函数 Circle c1 = new Circle(new Point(1.0,2.0), 2.0);

    2.1 Circle c1

    首先在栈内创建一个c1引用,指向地址不详

    未命名文件

    2.2 new Point(1.0,2.0)

    在堆内创建一个Point对象 ,开始时Point内的变量X Y皆为0,

    未命名文件 (1)

    在栈内创建x1,y1,然后将其赋值给Point的x y,构造完成时内存分配如图

    未命名文件 (1)

    构造函数运行完成后,栈内x1,y1空间立刻释放

    未命名文件 (1)

    2.3 在堆内创建Circle对象 new Circle(new Point(1.0,2.0), 2.0)

    同样,Circle对象刚创建时引用类型值为null,基础变量值为0,然后进入构造函数,在栈内创建一个指向刚才创建的Point对象的P和值为2.0的r,

    未命名文件 (3)

    然后用这两个元素初始化Circle内元素,使o指向Point,radius值变为2.0

    未命名文件 (4)

    构造函数完成后,栈内的r和p立刻释放

    最后将c1指向Circle对象 Circle c1 = new Circle(new Point(1.0,2.0), 2.0);

    未命名文件 (5)

    3. c1.getO().getX()

    首先调用geto获得c1所指向的Point,在栈中创建一个引用指向O引用

    然后调用getX(),返回其X值,在栈内创建一临时对象用于存储X值

    未命名文件 (6)

    打印完成后,临时对象会被清理

    4. c1.setO(5,6);

    经过前面的学习,相信这个就很简单了,

    在内存中创建临时变量x=5.0,y=6.0,然后通过赋值,改变c1内Point对象的值,SetO()结束后临时变量被清理.

    5. c1.increaseRadius().increaseRadius().area() 关于this

    首先c1调用increaseRadius()方法,返回一个Circle类的临时引用存储在栈内,指向c1对象,使radius变量+1.然后在通过这个临时引用再次调用increaseRadius()方法,使radius+1,再返回一个临时引用压栈,最后通过这个临时变量调用area()方法获取c1的增大半径后的面积.语句完成后两个栈内临时变量立刻被清理

    未命名文件 (7)

    四.static变量的内存分配

    1.代码

    class Cat {
        public static int num = 0;
        String name;
    
        Cat(String name) {
            this.name = name;
            ++num;
        }
    }
    public class Main {
        public static void main(String[] args) {
            Cat c1 = new Cat("mimi");
            Cat c2 = new Cat("miao");
            System.out.println(Cat.num);
        }
    }
    

    2.所有的字符串和static类型的静态成员变量都会预先加载于方法区内

    所以上述代码执行时,内存分布如图,可以认为堆内c1,c2各有一引用指向方法区内对应元素

    多态(动态绑定的内存分配)

    1.代码

    class Animal {
        public String name;
        Animal(String name) { this.name = name; }
        void enjoy() { System.out.println("叫声"); }
    }
    class Dog extends Animal {
        public String furName;    //毛皮颜色
        Dog(String name,String furName) { super(name); this.furName=furName;}
        @Override
        void enjoy() { System.out.println("汪汪汪"); }
    }
    class Cat extends Animal {
        Cat(String name) { super(name); }
        @Override
        void enjoy() { System.out.println("miao"); }
    }
    class lady {
        public String name;
        public Animal pet;
        lady(String name, Animal pet) {
            this.name = name;
            this.pet = pet;
        }
        void enjoy() { this.pet.enjoy(); }
        void replace_pet(Animal pet) { this.pet = pet; }
    }
    public class Main {
        public static void main(String[] args) {
            Dog dog = new Dog("小黄","yellow");
            lady w1 = new lady("张不悔", dog);
            w1.pet.enjoy();
            Cat cat = new Cat("大白","white");
            lady w2=new lady("刘静",cat);
            w2.pet.enjoy();
        }
    }
    

    2.运行时,内存分布大致如图

    在实际运行时,Lady内部的pet虽然是animal类型的引用,但是其指向的堆内存空间对象是一个完整的Dog对象,注意类内方法并不在堆内存储,类内方法存储在方法区内,堆中类对象只存储指向此方法的一个指针,因而打印出的enjoy结果是"汪汪",而不是"叫声".

    但是因为pet类型是Animal,所以通过pet引用只能访问Animal类拥有的方法和变量,比如w1.pet.furname是错误的.因为pet引用并不知道他所引用的对象究竟是dog还是cat,只能判断出其至少是个Animal对象.

    需要注意的是,栈内元素在使用完成后会立刻被清理,但是堆内创建的对象并不会立刻消失,而是在垃圾回收器启动回收后才会消失,而为了性能考虑,在内存充沛时,垃圾回收器并不会经常运行.所以可能某个元素已经空置了很久但是仍未被回收.
    
    class Point {
        int x, y;
    
        Point(int _x, int _y) {
            x = _x;
            y = _y;
        }        //_x,_y在构造函数运行完成后会立刻消失
    }
    
    public class Main {
        public static void main(String[] args) {
            Point p1 = new Point(1, 2);
            Point p2 = new Point(2, 3);
            p1 = p2;         //p1之前所指向的Point对象已经成为空置垃圾,但是不会被立刻回收
        }
    }
    
  • 相关阅读:
    delphi 相对路径
    delphi 安装.dpk;package
    SVN Unable to connect to a repository at UR
    android oncreate获取宽高度
    android 中Activity的onStart()和onResume()的区别是什么
    android canvas d
    android Bitmap类方法属性 详细说明
    android 读取根目录下的文件或文件夹
    Android UI编程(1)——九宫格(GridView)
    Android程序函数 将assets文件夹下的文件复制到手机的sd卡中(包括子文件夹)
  • 原文地址:https://www.cnblogs.com/INnoVationv2/p/12321197.html
Copyright © 2011-2022 走看看