Java学习日记 2022.6.27


1.什么是内存垃圾

Java的内存,主要分配给栈、堆和方法区。
栈的内存是固定的,只与类结构有关,在运行前就是确定的,在方法或线程结束时就能回收。
堆和方法区的内存是动态的,比如接口有不同的实现类,内存都不一样,比如方法可能走不同的逻辑分支,内存也不一样。所以堆和方法区的内存,只能在运行期间确定,必须动态地分配和回收,这就会产生垃圾。
要判断一个对象是不是垃圾,主要检查它有没有被引用。
简而言之就是已经使用完毕但是还没有释放的内存。

2.什么是垃圾回收 GC (garbage collection)

手动gc, JDK提供了一个gc函数, 调用这个函数, 就完成对JVM虚拟机内存进行一次GC
System.gc()
自动gc, 当JVM虚拟机启动后, 后台会自动运行一个gc程序, 负责内存的gc操作
这个后台的gc程序会定时做gc操作, 或者是当JVM内存不够用的时候, 会立刻触发gc

3.JDK自带的jvisualvm工具,安装Visual GC插件

在jdk的bin目录下有jvisualvm.exe,右击以管理员方法运行,在它的工具选项的插件选项中的可用插件中选择安装Visual GC.
在这里插入图片描述

4.JVM垃圾回收(GC)

Minor GC和Major GC/Full GC的区别

Minor GC 发生在新生代的GC, 速度很快
Major GC/Full GC发生在老年代的GC, 通常Major GC发生的时候都伴随着Minor GC的发生, 速度比较慢
示例:

public class Demo01 {
    //没有static修饰,data是成员变量,占用100Mb
    byte[] data = new byte[1024 * 1024 * 100];
    static Demo01 demo = null;  //点击运行时,类加载静态变量和静态代码块,出现在字节码的静态变量

    public static void main(String[] args) {    //类加载成功后,main()函数入栈执行
        sleep(20);      //slee入栈执行20秒后,出栈
        test01();       //test01入栈,出栈
        sleep(10);
        System.gc();    //调用垃圾回收
        System.out.println("finish");
        while (true){
        }//在这里进行死循环的目的是不让main函数结束执行
    }

    private static void test01() {      //new Demo01,会去找Demo01的构造函数压栈执行  ---> new Demo01的时候会 new byte
        Demo01 demo01 = new Demo01();   //test01 去堆里造对象,属性是data,data是有值的,是地址指向了一个数组
        demo = demo01;  //demo01在栈区中,demo在方法区中,把demo01赋给demo,因为test01在栈帧中,所以test01出栈后,demo01销毁
    }

    public static void sleep(int n){
        for (int i = 1; i <= n; i++) {
            System.out.print(i);
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

Java jvisualvm运行截图
在这里插入图片描述
以上述所说的程序作为示例,在test01出栈后,通过System.gc()调用了垃圾回收机制。但是相应的内存空间没有被释放的原因是局部变量demo01被赋值给了demo,而demo是静态变量,不会因为test01的出栈而消失,所以此时依然有指针指向Demo01这个对象,所以这个对象的内存空间不会被垃圾回收机制释放。
在JVM虚拟机执行GC的时候, 所有的线程都会暂停运行, 有一句叫做"stop the world!"
如果堆区空间一直不能回收则会产生OutOfMemoryError: Java heap space

5.对象与指针的关系

一个对象可以同时被多个指针指向;
一个指针可以指向不同的对象,但是同一时刻只能指向一个对象。

当对象有指针指向的时候, 对象不会被垃圾回收器回收;
当对象没有指针指向的时候, 对象会被垃圾回收器回收, 但是不会立即回收。

指针是可以传递的

从栈中传递到堆区

例如: 领养宠物成功后, 将宠物对象的指针传递到宠物数组中进行存储随机生成怪物对象后, 将怪物对象的指针传递到怪物数组中进行存储

从栈中传递到另一个栈中

例如: 宠物杀死怪物成功后, 调用宠物的killOk方法, 并把怪物的指针传递到killOk方法中, 这样可以在killOk方法中通过怪物的指针获取到被杀死的怪物对象中的信息

if (pkMonster.getHp() <= 0){
                pkdog.killok(pkMonster);
                break;
            }
----------------------------------------------------
public void killok(Monster monster){
        System.out.println(this.nickName + "成功击杀" + monster.getName());
        System.out.println("获取经验值:" + monster.getExp());
        this.exp += monster.getExp();
        if (this.exp >= this.maxExp){
            this.level++;
            this.exp = this.exp - this.maxExp;
            System.out.println(this.nickName + "升级到" + this.level + "级!");
        }
    }

禁止直接操作对象中的属性, 用成员方法封装操作对象属性的过程

对象中的属性被private封装了
所有操作都应该提供成员方法, 用指针去调用成员方法来操作
例如: 当宠物攻击或被攻击, 怪物攻击或被攻击, 都应该提供对应的成员方法进行调用

6.JVM运行时概况

程序运行时大致可以分为三块,分别可以称之为方法区、栈区、堆区。
在这里插入图片描述

堆区

堆区是jvm中栈内存最大的一块区域,他在虚拟机启动时创建,是所有线程所共享的一块区域。堆的任务就是存放所有的对象实例,几乎所有的对象实例都储存在这里。他可以分为年轻代、永生代,同时Java堆也是GC工作的主要区域,有时候我们也叫他GC堆。
新生代在进行GC时,会向两个S区转移对象,但是当两个S区都已经没有空间,那就无法将新生代的对象再转移到S区,这时候就会分配担保机制将新生代中的对象直接晋升到老年代。

年轻代

年轻代又可以分为三部分伊甸园区(Eden)、幸存者一区(S0)、幸存者二区(S1)默认情况下这三部分大小按照8:1:1(大小可调)分布,整个年轻代又占Java堆的1/3。
大多数的对象都在年轻代中创建,程序运行过程中很多对象都是朝生夕死。
小对象出生在伊甸园区, gc后, 如果能存活下来会被转移到幸存者区, 对象的年龄计数器会+1

老年代

当新生代的对象经历了15次回收后依然能够存活, 会进入老年代。
重对象/大对象一出生就在老年代。

栈区

虚拟机栈是每个Java方法的内存模型,虚拟机栈中元素叫做“栈帧”,每一个方法被执行的时候都会压入一个栈帧,执行完毕则出栈,这个栈帧里面存放着这个方法的局部变量表(包括参数)、操作栈、动态链接、方法返回地址。
我们需要知道的是,局部变量表是在编译的时候就确定大小了,我们在调用一个方法时,局部变量表的大小是已知且确定的,在方法执行的时候不会改变局部变量表的大小。
我们常说的Java内存中的栈内存一般就是指的局部变量表部分。如果变量是基本类型,会直接保存在这个区域,如果是引用类型,那会保存对象的引用地址。

元空间区

元数据区又被称为方法区,也可以叫作永久代。
方法区内存储着字节码文件和常量池;
它用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,是各个线程共享的内存区域。

7.商品管理练习

题目要求:
创建商品类Goods
属性  注意考虑商品属性的数据类型
商品名称 name
商品价格 price
商品描述 description
商品库存 stock
商品分类 category
商品状态 state

成员方法 show
  作用打印商品对象信息

构造方法
setter/getter方法

创建测试类Demo
静态变量声明商品数组, 初始长度为10
静态变量控制台输入

添加商品方法1 - addGoods1() 使用构造方法初始化商品对象数据 注意: 数组长度不够了要提示去扩容
	控制台接受用户输入商品信息
	使用全参构造方法构造商品对象
	商品对象存入商品数组中

添加商品方法 2 - addGoods2() 使用设值器set方法初始化商品对象数据 注意: 数组长度不够了要提示去扩容
	控制台接受用户输入商品信息
	使用无参构造方法构造商品对象
	使用商品对象的设值器set方法存入商品数据到商品对象中
	商品对象存入商品数组中    	

遍历商品方法 - display()   注意: 遇到数组中null的元素要判断, 防止出空指针异常
	遍历商品数组, 取出每个商品对象, 调用对象的show()方法打印商品信息

商品数组扩容方法

main方法
	使用do.while循环打印菜单
		1. 添加商品1
		2. 添加商品2
		3. 查看商品
		4. 商品数组扩容
		5. 退出
	接受用户输入的菜单编号
	使用switch.case 判断用户选择的菜单, 调用对应的方法

Goods.java

package Goods;

public class Goods {
    private String name;
    private Integer price;
    private String description;
    private Integer stock;
    private String category;
    private String state;

    public Goods() {
    }

    public Goods(String name, Integer price, String description, Integer stock, String category, String state) {
        this.name = name;
        this.price = price;
        this.description = description;
        this.stock = stock;
        this.category = category;
        this.state = state;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

Demo.java

package Goods;

import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType;

import java.util.Scanner;

public class Demo {
    static Goods[] goodsArr = new Goods[10];
    static Scanner sc = new Scanner(System.in);

    static String name;
    static Integer price;
    static String description;
    static Integer stock;
    static String category;
    static String state;

    public static void main(String[] args) {
        int choice;
        do {
            System.out.println("******商品管理系统********");
            System.out.println("*******1.添加商品1*******");
            System.out.println("*******2.添加商品2*******");
            System.out.println("*******3.查看商品********");
            System.out.println("*******4.商品数组扩容*****");
            System.out.println("*******5.退出!**********");
            System.out.println("请输入数字1~5进行选择:");
            choice = sc.nextInt();
            switch (choice){
                case 1:
                    System.out.println("添加商品1");
                    addGoods1();
                    break;
                case 2:
                    System.out.println("添加商品2");
                    addGoods2();
                    break;
                case 3:
                    System.out.println("查看商品");
                    display();
                    break;
                case 4:
                    System.out.println("商品数组扩容");
                    scale();
                    break;
                case 5:
                    System.out.println("商品管理系统已退出!");
                    break;
                default:
                    System.out.println("输入不合法!!!");
            }
        }while (choice != 5);
    }

    private static void scale() {
        Goods[] goodsArr2 = new Goods[goodsArr.length + 10];
        for (int i = 0; i < goodsArr.length; i++) {
            goodsArr2[i] = goodsArr[i]; 
        }
        goodsArr = goodsArr2;
        System.out.println("商品数组扩容+10,目前库存容量总数:" + goodsArr.length);
    }

    private static void addGoods2() {
        if (goodsArr[goodsArr.length - 1] != null){
            System.out.println("仓库已满请扩容!");
            return;
        }
        input();
        Goods goods = new Goods();
        goods.setName(name);
        goods.setPrice(price);
        goods.setDescription(description);
        goods.setCategory(category);
        goods.setState(state);
        for (int i = 0; i < goodsArr.length; i++) {
            if (goodsArr[i] == null){
                goodsArr[i] = goods;
                break;
            }
        }
    }

    private static void display(){
        for (int i = 0; i < goodsArr.length; i++) {
            if (goodsArr[i] == null){
                return;
            }else {
                show(goodsArr[i]);
            }
        }
    }

    private static void show(Goods goodArr) {
        System.out.println("*****************");
        System.out.println("商品名称:" + goodArr.getName());
        System.out.println("商品价格:" + goodArr.getPrice());
        System.out.println("商品描述:" + goodArr.getDescription());
        System.out.println("商品库存:" + goodArr.getStock());
        System.out.println("商品分类:" + goodArr.getCategory());
        System.out.println("商品状态:" + goodArr.getState());
    }

    private static void addGoods1() {
        if (goodsArr[goodsArr.length - 1] != null){
            System.out.println("仓库已满请扩容!");
            return;
        }
        input();
        Goods goods = new Goods(name,price,description,stock,category,state);
        for (int i = 0; i < goodsArr.length; i++) {
            if (goodsArr[i] == null){
                goodsArr[i] = goods;
                break;
            }
        }
    }

    private static void input(){
        System.out.println("请输入商品名称:");
        name = sc.next();
        System.out.println("请输入商品价格:");
        price = sc.nextInt();
        System.out.println("请输入商品描述:");
        description = sc.next();
        System.out.println("请输入商品库存:");
        stock = sc.nextInt();
        System.out.println("请输入商品分类:");
        category = sc.next();
        System.out.println("请输入商品状态:");
        state = sc.next();
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值