个人小结

2018-12-26  本文已影响0人  沈一飞3527

java的内存管理

任何语言的目的都是对一块磁盘的操作,所以开篇准备小讲一下内存管理机制,这篇作为一个基础篇;
jvm一共有九大部分构成,分别是:类加载子系统、方法区、堆、栈、直接内存、本地方法栈、垃圾回收系统、pc寄存器、执行引擎
先用一张图来展示下这5个常用模块:


JVM内存分配

共享内存

私有内存

其他子模块的功能

常用的JVM参数配置

堆分配参数
-XX:+PrintGC在eclipse中使用这个参数后;当虚拟机启动后,遇到GC就会打印相关日志
-XX:+UseSerialGC:配置串行回收器(当然:还有并行的回收器)
-XX:+PrintGCDetails可以查看详情信息,包括各个区的情况
-Xms:设置java程序启动时初始堆的大小
-Xmx:设置java程序能获得的最大堆大小
-Xmx20m -Xms5m -XX:+PrintCommandLineFlags:可以将隐式或者显示传给虚拟机的参数输出
eclipse参数配置:Run-Run Configurations-Arguments Vmarguements
将配置好的jvm参数贴上去
-XX:+PrintGC -Xms5m -Xmx20m -XX:+UseSerialGC -XX:+PrintGCDetails
含义:初始堆内存5M,最大堆内存20M
可以看下下面demo的实验效果

public static void main(String[] args) {        
        System.out.println("最大内存:"+Runtime.getRuntime().maxMemory());
        System.out.println("可使用内存:"+Runtime.getRuntime().freeMemory());
        System.out.println("总内存:"+Runtime.getRuntime().totalMemory());
        byte[] b1=new byte[1*1024*1024];
        System.out.println("分配了1M后的内存");
        System.out.println("最大内存:"+Runtime.getRuntime().maxMemory());
        System.out.println("可使用内存:"+Runtime.getRuntime().freeMemory());
        System.out.println("总内存:"+Runtime.getRuntime().totalMemory());
        byte[] b2=new byte[4*1024*1024];
        System.out.println("分配了4M");
        System.out.println("最大内存:"+Runtime.getRuntime().maxMemory());
        System.out.println("可使用内存:"+Runtime.getRuntime().freeMemory());
        System.out.println("总内存:"+Runtime.getRuntime().totalMemory());
    }

思考:根据jvm的垃圾回收机制,如何配置初始化内存和最大内存才能将jvm调优性能最好呢?

新生代参数配置修改
-Xmn:可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大的影响
-XX:SurvivorRatio:用来设置新生代中的eden空间和from到to空间的比例,即:-XX:SurvivorRatio=eden/from=eden/to;
-XX:NewRatio=老年代/新生代
推荐值:新生代大小一般设置为整个堆空间的1/3到1/4左右
修改jvm内存设置为:
-Xms20m -Xmx20m -XX:NewRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
含义:初始为20M,最大为20M,新生代/老年代=2
观察GC的详细信息输出

public static void main(String[] args) {
        byte[] b=null;
        for(int i=0;i<10;i++){
            //分了10M的内存
            b=new byte[1*1024*1024];
        }
    }

jvm中的运行流程

classloader:动态加载某个class文件到内存(jvm)当中,我们日常编程通常是.java文件,而通过jdk中的javac指令可以将.java文件编译为.class文件,这个时候这个类还不能被其它类引用,还需要classloader,将class文件加载到内存中,方可以被其他类使用,
类的字节码文件被加载器(classloader)先装到运行时数据区,然后执行引擎,经过调用本地接口,放入本地方法库
类的装载:加载、连接(验证、准备、解析) 初始化,使用、卸载
,class 在堆中保存类的定义或者结构,

类的初始化:执行类的构造器(clinit):为类的静态变量赋予正确的初始值,其中如果这个类中有静态块或者静态变量,则执行顺序已该类中排列的顺序前后,在静态块中的变量可以赋值但是不能读,先执行构造器,再执行构造方法,构造方法的作用就是实例化对象

构造器包括:
1:static 变量
2:静态块

public class Demo {
    
    public static int temp=1;                                   
    
    static{
        temp=3;
        System.out.println(temp);
    }
    public static void main(String[] args) {
        temp=5;
        System.out.println(temp);
    }
}

上述类可以分别打上断点,实验一下,可以观察下类的执行过程,先执行的是静态变量赋值,而后执行的是静态块,再进行的是main方法;

classLoader

jdk的类加载器:

public class Demo {

    public static void main(String[] args) {
        //打印当前的类加载器
        System.out.println(Demo.class.getClassLoader());
        //sun.misc.Launcher$AppClassLoader@73d16e93
        //打印父加载器
        ClassLoader loader=Demo.class.getClassLoader();
        while(loader!=null){
            System.out.println(loader);
            loader=loader.getParent();
        }
//      sun.misc.Launcher$AppClassLoader@73d16e93
//      sun.misc.Launcher$AppClassLoader@73d16e93
//      sun.misc.Launcher$ExtClassLoader@15db9742
        //null:当为null时,说明指向的是BootStarp加载器
        System.out.println(loader);
    }
}

我们可以看下classLoader的源码,把jdk源码导入eclispe中后,可以看到,这个类位于rt.jar中,是个抽象类,真正的逻辑是在loadClass(String name,boolean resolve)中处理,该方法的逻辑是:先去看下这个类是否已经加载过了,如果加载过了,就返回加载过的类,如果没有的话,则看下父类加载器是否由加载过,如果父类加载器也没有加载,则去Bootstreap中看下是否有加载,这个判断,在jvm中有个学名叫做:双亲委派模式,如果都没有加载过,则选择自定义的加载器,开始加载这个类,如果刚开始都没有发现这个类的全名,就会抛出著名的classNotFroundExcetpion
而点击findClass方法,可以看到:没有具体逻辑,why?

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

这个方法是需要子类重写的,重写这个方法可以自定义加载器,你也可以写个~

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

暂时理解这么多,再有新的理解再更新喽

上一篇下一篇

猜你喜欢

热点阅读