JAVA提高(四)--- JVM部分

1、如何实现平台无关性

javac 对代码进行编译、javap -c对代码进行反汇编获得JVM指令、java指令执行class文件。
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java在不同的平台运行时不需要进行编译,Java虚拟机在执行字节码文件的时候,把字节码转换成具体平台的机器指令。
不直接将源码转换成机器码是为了:不让每次执行时都要检查语法等,影响性能;也可以将别的语言解析字节码。

2、JVM如何加载class文件

在这里插入图片描述

3、反射

反射是指Java反射机在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象都能够调用他的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制。
使用反射:(Person是自定义类,注意要有无参构造方法)

public class Person {

	private String name;
	public int age;
	
	public Person() {}
	
	public Person(String name, int age) {
		this.age = age;
		this.name = name;
	}
	
	public Person(String name) {
		this.name = name;
		this.age = 0;
	}
	
	private void sayHi(String hi) {
		System.out.println("sayHi:" + hi);
	}
	
	public void sayHello(String hello) {
		System.out.println(hello + ":" + name);
	}

	@Override
	public String toString() {
		return "name = " + this.name + " age = " + this.age;
	}
}

测试类

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
	
		Class class1 = Class.forName("study.Person");
		Person person = (Person) class1.newInstance();
		System.out.println("Class name is " + class1.getName());
		//获取类里面的所有方法包括私有的方法,但不能获取继承的和接口的方法
		Method getHi = class1.getDeclaredMethod("sayHi", String.class);
		//暴力访问,可以访问私有方法
		getHi.setAccessible(true);
		getHi.invoke(person, "嗨");
		
		//另一种获取方法的方法,只能获取public的方法和继承的和接口的方法
		Method getHello = class1.getMethod("sayHello", String.class);
		getHello.invoke(person, "你好");
		
		//获取私有属性
		Field name = class1.getDeclaredField("name");
		name.setAccessible(true);
		name.set(person, "Luffykaiyuan");
		getHello.invoke(person, "你好");
		
		//获取构造函数
		Constructor<?> cons = class1.getConstructor(String.class, int.class);
		Person person2 = (Person) cons.newInstance("Luffy", 1);
		System.out.println(person2.toString());
	}
}

4、classLoader

类从编译到执行的过程:
(一)编译器将Person.java原文件编译成Person.class字节码文件
(二)ClassLoader将字节码转换为JVM中的Class对象
(三)JVM利用Class对象实例化为Person对象

ClassLoader主要工作在Class装载阶段,其主要作用是从系统外部获得Class二进制数据流。他是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过Class里的二进制数据流装载进系统,然后JVM进行连接、初始化等操作。
种类:(一)BootStrapClassLoader:C++编写的,加载核心库,例如:java.lang.
(二)ExtClassLoader:java编写,加载扩展库,例如javax。加载\lib\ext目录下的类库
(三)AppClassLoader:java编写,加载classpath下的class
(四)自定义ClassLoader:java编写,定制化加载
自定义ClassLoader的实现:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    private String path;
    private String classLoaderName;

    public MyClassLoader(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }

    //用于寻找类文件
    @Override
    public Class findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }

    //用于加载类文件
    private byte[] loadClassData(String name) {
        name = path + name + ".class";
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = new FileInputStream(new File(name));
            out = new ByteArrayOutputStream();
            int i = 0;
            while ((i = in.read()) != -1) {
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }
}

5、双亲委派机制

在这里插入图片描述
能够避免多份同样的字节码的加载。

6、loadClass与forName的区别

隐式加载:new的方式,可以调用有参构造器。
显示加载:loadClass、forName等。获取到Class对象,调用Class的newInstance方法创建,不支持调用有参数构造器,除非使用反射。

类装载过程:
(一)加载:通过ClassLoader加载class文件字节码到内存中,生成class对象。
(二)连接:1、校验:检查加载的class的正确性和安全性
2、准备:为类变量分配存储空间并设置类变量初始值
3、解析:JVM将常量池内的符号引用转换为直接引用
(三)初始化:执行类变量赋值和静态代码块

loadClass与forName的区别:
(一)ClassLoader.forName会初始化类,ClassLoader.loadClass不会。
(注:数据库创建数据库驱动是需要用forName,因为Driver里面有静态代码块。
Spring中读取bean的配置的时候需要用loadClass加载,因为延时加载,不需要执行类中的初始化代码,加快速度)

7、Java内存模型——JDK8

线程私有:程序计数器、虚拟机栈、本地方法栈
线程共享:常量池、堆
(一)程序计数器:当前线程所执行的字节码行号指示器(逻辑计数器,不是物理计数器),告诉虚拟机执行到哪里了。对Java方法计数,对Native方法值为Undefined。不会发生内存泄漏。
(二)虚拟机栈:包含单个线程每个方法执行的栈帧,栈帧包含了局部变量表、操作栈、动态链接、返回地址。方法结束后,自动释放掉。
局部变量表:包含方法执行过程中的所有变量
操作栈:执行字节码指令中被用到
在这里插入图片描述
局部变量栈为操作数栈提供必要的数据支撑。
引发java.lang.SrackOverflowError的原因:递归过深,栈帧书超过虚拟栈深度。解决:限制递归次数,或用循环代替递归。
引发java.lang.OutOfMemoryError的原因:虚拟机栈可以动态扩展时,申请不到足够多的内存。
(三)本地方法栈
与虚拟机类似,主要作用于标注了native的方法
(四)元空间(MetaSpace)与永久代
元空间使用本地内存存储class相关信息,解决空间不足的问题。
元空间的优势:
1、字符串的常量池存在永久代,容易出现性能问题和内存溢出,所以移到了堆中。
2、类和方法的大小难以确定,给永久代的大小指定带来困难。
3、永久代会为GC带来不必要的复杂度
(五)Java堆
对象实例的分配区域。GC管理的主要区域,内部分为新生代与老年代。

8、常见问题

(一)JVM三大性能调优参数-xms -xmx -xss的含义
-xss:规定了每个线程虚拟机栈的大小,影响此进程中并发线程的大小
-xms:堆的初始值
-xmx:堆能扩展到的最大值(一般xmx=xms,避免扩容导致内存抖动,影响稳定性)
(二)Java中堆和栈的区别
静态存储:编译时确定每个数据目标在运行时的存储空间需求
栈式存储:数据区需求在编译时未知,运行时模板入口前确定
堆式存储:编译时或运行时模块入口处都无法确定,动态分配
在这里插入图片描述
区别:
管理方式:栈自动释放,堆需要GC
空间大小:一般情况下,栈比堆小
碎片相关:栈产生的碎片远小于堆
分配方式:栈支持静态分配和动态分配,堆仅支持动态分配
效率:栈的效率比堆高

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值