我对Java语言的理解

本文深入探讨Java平台无关性、JVM加载机制、反射原理、类加载器及其双亲委派机制,剖析Java内存模型,旨在帮助读者理解Java核心技术,提升实战能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        去年七月初通过校招进入了现在的公司,转眼已经过去一年多了,经过一年多的实战开发,重新整理一下自己对java的理解,希望能对正在面试以及即将入职工作的blogger有所帮助。

1、平台无关性

        Java源码首先被编译成字节码,再由不同的平台的JVM(Java Virtual Machine)进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。

        下面我们简单看一下jvm执行的字节码,命令 javap

public class T {
	public static void main(String[] args) {
		int i = 1;
		System.out.println(i++);
	}
}

       使用 javap查看字节码

2、JVM加载class文件

        JVM如何加载编译好的class文件呢?

        Java虚拟机:是一种抽象化的虚拟机,通过在实际计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统,JVM屏蔽了与具体操作系统windows、linux等相关的信息,使得Java程序只需生成在Java虚拟机上运行的代码及字节码,就可以在多种平台上不加修改的运行。

        通常呢,我们不需要知道JVM的运行原理,只需要专注于Java代码就可以了,当然,这也是虚拟机之所以存在的原因,即屏蔽底层操作系统的不同,并且,减少基于原生语言的开发的复杂性。只要虚拟机厂商在特定操作系统上实现了虚拟机,定义如何将字节码解析成本操作系统可执行的二进制码,java语言便能跨越各种的平台。

        JVM的架构 = Class Loader + Runtime Data Access + Execution Engine + Native Interface

        Class Loader: 依据特定格式,加载class文件到内存。

        Execution Engine: 对命令进行解析,即解析class文件中的字节码,解析完成之后,提交操作系统执行。

        Runtime Data Area: JVM内存空间结构模型,我们所写的程序都会被加载到这里,之后才开始运行。

        Native Interface: 融合不同开发语言的原生库为Java所用,JVM开辟了一块区域用来专门处理标记为native的代码,具体做法是,在Native Method Stack中登记native方法,在Excution执行时,加载Native Libraries。

        Class.forName中调用的forName0

    /**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName(className, true, currentLoader)}
     * </blockquote>
     *
     * where {@code currentLoader} denotes the defining class loader of
     * the current class.
     *
     * <p> For example, the following code fragment returns the
     * runtime {@code Class} descriptor for the class named
     * {@code java.lang.Thread}:
     *
     * <blockquote>
     *   {@code Class t = Class.forName("java.lang.Thread")}
     * </blockquote>
     * <p>
     * A call to {@code forName("X")} causes the class named
     * {@code X} to be initialized.
     *
     * @param      className   the fully qualified name of the desired class.
     * @return     the {@code Class} object for the class with the
     *             specified name.
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located
     */
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
    /** Called after security check for system loader access checks have been made. */
    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
        throws ClassNotFoundException;

3、谈谈反射

        Java反射机制是在运行状态中,对任意一个类,都能够知道这个类的所有属性和方法;对任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

        简单例子

public class Dog {

    private String name;

    public void sayHi(String hi) {
        System.out.println(hi + " " + name);
    }

    private String returnHello(String hello) {
        return hello;
    }
}
public class ReflectDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        Class c = Class.forName("Dog");
        if(!c.isAssignableFrom(Dog.class))
            return ;
        Dog dog = (Dog) c.newInstance();
        System.out.println("Class name is " + c.getName());
        Method returnHello = c.getDeclaredMethod("returnHello", String.class);
        returnHello.setAccessible(true);
        String str = (String) returnHello.invoke(dog, "ao wu");
        System.out.println(str);
        Method sayHi = c.getMethod("sayHi", String.class);
        sayHi.invoke(dog, "hi");
        Field name = c.getDeclaredField("name");
        name.setAccessible(true);
        name.set(dog, "Doge");
        sayHi.invoke(dog, "hi");
    }
}

        编译运行

Class name is Dog
ao wu
hi null
hi Doge

        通过这个例子,我们可以知道,反射就是把Java类中的各种成分映射成一个个Java对象,Method、Field、Class。

4、ClassLoader

        在上面我们之所以能够获得类的属性或者方法,并对其进行调用,必须要获取Class对象,而要获取该类的Class对象必先获取该类所对应字节码文件对象。

        编译器将Dog.java源文件编译成Dog.class字节码文件。

        ClassLoader将字节码转换成JVM中的Class<Dog>对象。

        JVM利用Class<Dog>对象实例化为Dog对象。

        ClassLoader: ClassLoader在Java中有着非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装进系统,然后交给Java虚拟机进行连接、初始化等操作。

        ClassLoader的种类

                BootStrapClassLoader: C++编写,加载Java自带的核心类库java.*,比如java.lang包。

                ExtClassLoader: Java编写,加载扩展库javax.*,加载位于jre/lib/ext目录下的jar包。

                AppClassLoader: Java编写,加载程序所在目录。

                自定义ClassLoader: Java编写,定制化加载。

        我们可以看一下ExtClassLoader的代码

private static File[] getExtDirs() {
            String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
            if (var0 != null) {
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens();
                var1 = new File[var3];

                for(int var4 = 0; var4 < var3; ++var4) {
                    var1[var4] = new File(var2.nextToken());
                }
            } else {
                var1 = new File[0];
            }

            return var1;
        }

        可以看一下 "java.ext.dirs" 的路径

System.out.println(System.getProperty("java.ext.dirs"));


C:\ProgramFiles\Java\jdk1.8.0_131\jre\lib\ext;
C:\Windows\Sun\Java\lib\ext

        我们可以看一下AppClassLoader的代码

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        可以看一下 "java.class.path" 的路径

System.out.println(System.getProperty("java.class.path"));


C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar
;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
D:\IdeaProjects\JavaStudy\target\classes;
D:\maven\repository\org\projectlombok\lombok\1.18.4\lombok-1.18.4.jar;
D:\maven\repository\org\springframework\boot\spring-boot-starter-security\2.1.6.RELEASE\spring-boot-starter-security-2.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\boot\spring-boot-starter\2.1.6.RELEASE\spring-boot-starter-2.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\boot\spring-boot\2.1.6.RELEASE\spring-boot-2.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.6.RELEASE\spring-boot-autoconfigure-2.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\boot\spring-boot-starter-logging\2.1.6.RELEASE\spring-boot-starter-logging-2.1.6.RELEASE.jar;
D:\maven\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;
D:\maven\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;
D:\maven\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;
D:\maven\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;
D:\maven\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;D:\maven\repository\org\slf4j\jul-to-slf4j\1.7.26\jul-to-slf4j-1.7.26.jar;
D:\maven\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;
D:\maven\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;D:\maven\repository\org\springframework\spring-aop\5.1.8.RELEASE\spring-aop-5.1.8.RELEASE.jar;
D:\maven\repository\org\springframework\spring-beans\5.1.8.RELEASE\spring-beans-5.1.8.RELEASE.jar;
D:\maven\repository\org\springframework\security\spring-security-config\5.1.5.RELEASE\spring-security-config-5.1.5.RELEASE.jar;
D:\maven\repository\org\springframework\security\spring-security-core\5.1.5.RELEASE\spring-security-core-5.1.5.RELEASE.jar;
D:\maven\repository\org\springframework\spring-context\5.1.6.RELEASE\spring-context-5.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\security\spring-security-web\5.1.5.RELEASE\spring-security-web-5.1.5.RELEASE.jar;
D:\maven\repository\org\springframework\spring-expression\5.1.6.RELEASE\spring-expression-5.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\spring-web\5.1.6.RELEASE\spring-web-5.1.6.RELEASE.jar;
D:\maven\repository\org\springframework\spring-core\5.1.5.RELEASE\spring-core-5.1.5.RELEASE.jar;
D:\maven\repository\org\springframework\spring-jcl\5.1.5.RELEASE\spring-jcl-5.1.5.RELEASE.jar;
C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.1\lib\idea_rt.jar

        自定义ClassLoader的实现

        关键函数 findClass、defineClass

/**
     * Finds the class with the specified <a href="#name">binary name</a>.
     * This method should be overridden by class loader implementations that
     * follow the delegation model for loading classes, and will be invoked by
     * the {@link #loadClass <tt>loadClass</tt>} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a <tt>ClassNotFoundException</tt>.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

/**
     * Converts an array of bytes into an instance of class <tt>Class</tt>.
     * Before the <tt>Class</tt> can be used it must be resolved.
     *
     * <p> This method assigns a default {@link java.security.ProtectionDomain
     * <tt>ProtectionDomain</tt>} to the newly defined class.  The
     * <tt>ProtectionDomain</tt> is effectively granted the same set of
     * permissions returned when {@link
     * java.security.Policy#getPermissions(java.security.CodeSource)
     * <tt>Policy.getPolicy().getPermissions(new CodeSource(null, null))</tt>}
     * is invoked.  The default domain is created on the first invocation of
     * {@link #defineClass(String, byte[], int, int) <tt>defineClass</tt>},
     * and re-used on subsequent invocations.
     *
     * <p> To assign a specific <tt>ProtectionDomain</tt> to the class, use
     * the {@link #defineClass(String, byte[], int, int,
     * java.security.ProtectionDomain) <tt>defineClass</tt>} method that takes a
     * <tt>ProtectionDomain</tt> as one of its arguments.  </p>
     *
     * @param  name
     *         The expected <a href="#name">binary name</a> of the class, or
     *         <tt>null</tt> if not known
     *
     * @param  b
     *         The bytes that make up the class data.  The bytes in positions
     *         <tt>off</tt> through <tt>off+len-1</tt> should have the format
     *         of a valid class file as defined by
     *         <cite>The Java&trade; Virtual Machine Specification</cite>.
     *
     * @param  off
     *         The start offset in <tt>b</tt> of the class data
     *
     * @param  len
     *         The length of the class data
     *
     * @return  The <tt>Class</tt> object that was created from the specified
     *          class data.
     *
     * @throws  ClassFormatError
     *          If the data did not contain a valid class
     *
     * @throws  IndexOutOfBoundsException
     *          If either <tt>off</tt> or <tt>len</tt> is negative, or if
     *          <tt>off+len</tt> is greater than <tt>b.length</tt>.
     *
     * @throws  SecurityException
     *          If an attempt is made to add this class to a package that
     *          contains classes that were signed by a different set of
     *          certificates than this class (which is unsigned), or if
     *          <tt>name</tt> begins with "<tt>java.</tt>".
     *
     * @see  #loadClass(String, boolean)
     * @see  #resolveClass(Class)
     * @see  java.security.CodeSource
     * @see  java.security.SecureClassLoader
     *
     * @since  1.1
     */
    protected final Class<?> defineClass(String name, byte[] b, int off, int len)
        throws ClassFormatError
    {
        return defineClass(name, b, off, len, null);
    }

        自定义装载简单demo

public class T {
	static {
		System.out.println("Hello I'm T");
	}
}

 

import java.io.*;

public class MyClassLoader extends ClassLoader {

    private String path;
    private String classLoadername;

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

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = classLoaderName(name);
        return defineClass(name, bytes, 0, bytes.length);
    }

    private byte[] classLoaderName(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 (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }

    public String getClassLoadername() {
        return classLoadername;
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader("C:\\Users\\Administrator\\Desktop\\", "myClassLoader");
        Class c = myClassLoader.loadClass("T");
        System.out.println(myClassLoader.getClassLoadername());
        System.out.println(myClassLoader.getParent());
        System.out.println(myClassLoader.getParent().getParent());
        System.out.println(myClassLoader.getParent().getParent().getParent()); 
        c.newInstance();
    }
}

        运行测试main函数

myClassLoader
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@5ca881b5
null
Hello I'm T

进程完成,退出码 0

5、类加载器的双亲委派机制

        不同ClassLoader加载路径不同,逻辑明确,为了实现分工,各自负责各自的区块。

        ClassLoader的loadClass源码如下,感兴趣可以了解一下。

    /**
     * Loads the class with the specified <a href="#name">binary name</a>.  The
     * default implementation of this method searches for classes in the
     * following order:
     *
     * <ol>
     *
     *   <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
     *   has already been loaded.  </p></li>
     *
     *   <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
     *   on the parent class loader.  If the parent is <tt>null</tt> the class
     *   loader built-in to the virtual machine is used, instead.  </p></li>
     *
     *   <li><p> Invoke the {@link #findClass(String)} method to find the
     *   class.  </p></li>
     *
     * </ol>
     *
     * <p> If the class was found using the above steps, and the
     * <tt>resolve</tt> flag is true, this method will then invoke the {@link
     * #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
     *
     * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
     * #findClass(String)}, rather than this method.  </p>
     *
     * <p> Unless overridden, this method synchronizes on the result of
     * {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
     * during the entire class loading process.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @param  resolve
     *         If <tt>true</tt> then resolve the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     */
    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;
        }
    }

        自底向上检查名字为name的类是否已被装载,如果没有,自顶向下装载名字为name的类,关键代码如下:

                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }

        上述代码也正可以和输出结果做对比,当parent为null时,则会调用我们的BootStrapClassLoader。

myClassLoader
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@5ca881b5
null
Hello I'm T

进程完成,退出码 0
    // return null if not found
    private native Class<?> findBootstrapClass(String name);

        注意,findBootstrapClass为native标记的方法,在运行时,会调用本地库或外置的非Java代码。

        如何确定findBootstrapClass为C++代码呢?查看网址http://hg.openjdk.java.net/,一下为ClassLoader.c截图

        查看源码之后,再来回答一下,为什么要使用双亲委派机制:避免多分同样字节码的加载。

6、loadClass和forName的区别

        隐式加载: new,隐式调用类加载器,加载对应类,创建对应实例。

        显式加载: loadClass、forName等,显式加载构建Class对象,然后调用Class的newInstance创建实例。

        loadClass和forName: 都能知道该类的所有属性和方法,对于任一对象都能调用它的任意防范和属性。

        loadClass: ClassLoader.loadClass得到的class是还没有链接的。

        forName: Class.forName得到的class是已经初始化完成的。

        类的装载过程

        来看一下下面的小demo

public class T {
	static {
		System.out.println("Hello I'm T");
	}
}
public class T2 {
	public static void main(String[] args) throws ClassNotFoundException {
		System.out.println("我是ClassLoader.loadClass");
		ClassLoader cl = T.class.getClassLoader();
        Class<T> tLoadClass = (Class<T>) cl.loadClass("T");
		System.out.println("我是Class.forName");
		Class<T> tForName = (Class<T>) Class.forName("T");
	}
}

        运行T2的main方法:

我是ClassLoader.loadClass
我是Class.forName
Hello I'm T

         从上述执行结果,我们可知道,Class.forName得到的class是已经初始化完成的,ClassLoader.loadClass得到的class是还没有链接的。比如,我们在程序中要加载我们的数据库驱动Driver,我们要使Class.forName。而为什么还会有ClassLoader.loadClass,主要是与LazyLoading有关,ClassLoader.loadClass不需要执行类的链接和初始化,加快了初始化速度。

7、Java的内存模型

        线程私有:程序计数器、虚拟机栈、本地方法栈。

        线程共享:MetaSpace、Java堆。

        Java虚拟机栈(Stack)

                Java方法执行的内存模型。

                包含多个栈帧。

        局部变量表和操作数栈

                局部变量表:包含方法执行过程中的所有变量。

                操作数栈:入栈、出栈、复制、交换、产生消费变量。

        递归为什么会引发 java.lang.StackOverflowError 异常: 当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压入虚拟机栈中,当方法执行完,便会将栈帧出栈,因此可知,线程当前执行的方法对应的栈帧位于Java栈的顶部,而我们的递归函数不断的去调用自身,每一次方法调用会涉及:(1)每新调用一个方法就会生成一个栈帧;(2)会保存当前方法的栈帧状态,将它放在虚拟机栈中;(3)栈帧上下文切换,会切换到最新的方法栈中,而我们每个虚拟机栈是固定的,递归过深,栈帧数超过虚拟机栈深度。解决思路:(1)限制递归次数;(2)使用循环替换递归等等。

        虚拟机栈过多引发 java.lang.OutOfMemoryError异常:  当虚拟机栈可以动态扩展时,如果无法申请足够多的内存,就会抛出这个异常。

    public void stackLeakByThread() {
        while(true) {
            new Thread(() -> {
                while(true) {}
            }).start();
        }
    }

        本地方法栈

                与虚拟机相似,主要作用于标注了native的方法。

        元空间(MetaSpace)与永久代(PermGen)

        存储class的相关信息,包括class的method、field等等,两者均是方法区的实现,方法区知识JVM的一种规范。Java1.7之后,原先位于方法区中的字符串常量池已被移动到了Java堆当中,并且,1.8之后,使用元空间替代了永久代。                                                                                                                                                                    元空间使用本地内存,而永久代使用的是jvm的内存。

        Java堆(Heap)

                对象实例的分配区域

                GC管理的主要区域

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值