面试必问——jvm原理及优化(包括GC)

jvm介绍

java从编码到运行
在这里插入图片描述
java文件被编译成class后,jvm先装载java类库,然后读取class,类加载器(ClassLoader)读取class文件,通过字节编码器和即时编译器(JIT)编译class,然后把编译后的数据丢到执行引擎去执行,执行引擎调用操作系统(OS)
经常使用到的代码,jit会编译成本地文件后由执行引擎执行。非常用的代码则有字节码解释器解释执行

jvm是规则,jvm与java无关

任何语言,只要能编译成*.class,就能在jvm上运行

常见的jvm实现

Hotspot

orcale官方,也是最常用的jvm,与Jrockit合并

命令行窗口
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

第一行是版本号,此jdk版本号为1.8.0_271
第二行是运行环境
第三行——HotSpot是此jvm的品牌名, 64-Bit=位数 Server=型号,mixed mode=执行模式(解释执行+编译执行)

Jrockit

BEA,曾经号称世界上最快的jvm,被oracle收购,合并于Hotspot

J9

IBM的jvm实现

Microsoft VM

微软的jvm实现

TaobaoVM

Hotspot的定制版,淘宝专用

LiquidVM

直怼硬件

azul zing(收费)

垃圾回收的业界标杆

class被编译成字节流的解读

java

package com.me.zookeeper;

import com.me.zookeeper.config.TestDistributedService;
import com.me.zookeeper.util.ZookeeperUtil;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

/**
 * zookeeper客户端A
 *
 * @author admin
 */
public class ZookeeperMainA {

    private final static CountDownLatch countDownLatch = new CountDownLatch(1);


    public static void main(String[] args) {
        System.out.println("服务A启动了");

        //zookeeper是有session概念,没有连接池的概念

        //new Zookeeper(zookeeper地址url,session超时时间(毫秒))
        // watch分两类(new zookeeper()创建的,属于session级别,与path和node没关系。)
        try {
            ZooKeeper zooKeeper = new ZooKeeper("106.53.22.186:2182,106.53.22.186:2181,106.53.22.186:2183/service", 100000, watchedEvent -> {
                switchZookeeperEventState(watchedEvent);
                countDownLatch.countDown();
            });
            switch (zooKeeper.getState()) {
                case CONNECTING:
                    System.out.println("zookeeper正在连接");
                    break;
                case ASSOCIATING:
                    break;
                case CONNECTED:
                    System.out.println("zookeeper已连接");
                    break;
                case CONNECTEDREADONLY:
                    System.out.println("打开zookeeper只读连接");
                    break;
                case CLOSED:
                    System.out.println("zookeeper连接关闭");
                    break;
                case AUTH_FAILED:
                    System.out.println("校验权限失败");
                    break;
                case NOT_CONNECTED:
                    System.out.println("没有连接");
                    break;
                default:
                    break;
            }
            countDownLatch.await();
            Stat stat = new Stat();
            //创建节点和数据
            String path = ZookeeperUtil.createTemporaryData("/service-A", "192.168.1.1");
            System.out.println(path);
            //同步获取节点数据
            TestDistributedService.test();
            String data = ZookeeperUtil.syncGetData("/service-A", stat);
            System.out.println(data);
            System.out.println(stat);
            //修改节点数据
            System.in.read();

            ZookeeperUtil.syncSetData(path, "192.168.1.1:8789", stat);
            stat.setVersion(stat.getVersion() + 1);
            System.out.println(ZookeeperUtil.syncGetData(path, stat));
            //删除节点
            ZookeeperUtil.deleteNode(path, stat);

            System.in.read();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void switchZookeeperEventState(WatchedEvent watchedEvent) {

        switch (watchedEvent.getType()) {
            case None:
                System.out.println("没有发生改变");
                break;
            case NodeCreated:
                System.out.println(watchedEvent.getPath() + "znode发生了create操作");
                break;
            case NodeDeleted:
                System.out.println(watchedEvent.getPath() + "znode的数据发生了delete操作");
                break;
            case NodeDataChanged:
                System.out.println(watchedEvent.getPath() + "znode的数据发生了change操作");
                break;
            case NodeChildrenChanged:
                System.out.println(watchedEvent.getPath() + "znode的子节点发生了变化");
                break;
            case DataWatchRemoved:
                System.out.println(watchedEvent.getPath() + "znode节点被移除");
                break;
            case ChildWatchRemoved:
                System.out.println(watchedEvent.getPath() + "znode子节点的watcher对象被移除");
                break;
            case PersistentWatchRemoved:
                System.out.println(watchedEvent.getPath() + "watcher对象被移除");
                break;
            default:
                break;
        }
        switch (watchedEvent.getState()) {
            case Disconnected:
                System.out.println("Disconnected");
                break;
            case SyncConnected:
                System.out.println("同步连接");
                break;
            case AuthFailed:
                System.out.println("验证失败");
                break;
            case ConnectedReadOnly:
                System.out.println("打开只读连接");
                break;
            case SaslAuthenticated:
                System.out.println("SaslAuthenticated");
                break;
            case Expired:
                System.out.println("expired");
                break;
            case Closed:
                System.out.println("关闭连接");
                break;
            default:
                break;
        }
    }
}

javap -v xxx.class查看class文件流的类型
E:\work\zookeeper-test-main\target\classes\com\me\zookeeper>javap -v ZookeeperMainB.class
Classfile /E:/work/zookeeper-test-main/target/classes/com/me/zookeeper/ZookeeperMainB.class
  Last modified 2021-4-30; size 419 bytes
  MD5 checksum 9b5bafe1edaf7cfd5ff666cf3696ec71
  Compiled from "ZookeeperMainB.java"
public class com.me.zookeeper.ZookeeperMainB
  minor version: 0
  major version: 52
  # class修饰符
  flags: ACC_PUBLIC, ACC_SUPER
# 常量池
Constant pool:
   #1 = Methodref          #3.#17         // java/lang/Object."<init>":()V
   #2 = Class              #18            // com/me/zookeeper/ZookeeperMainB
   #3 = Class              #19            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               Lcom/me/zookeeper/ZookeeperMainB;
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               args
  #14 = Utf8               [Ljava/lang/String;
  #15 = Utf8               SourceFile
  #16 = Utf8               ZookeeperMainB.java
  #17 = NameAndType        #4:#5          // "<init>":()V
  #18 = Utf8               com/me/zookeeper/ZookeeperMainB
  #19 = Utf8               java/lang/Object
{
  public com.me.zookeeper.ZookeeperMainB();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/me/zookeeper/ZookeeperMainB;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;
}
SourceFile: "ZookeeperMainB.java"
编译成二进制文件后

![image.png](https://img-blog.csdnimg.cn/img_convert/08ac8badd1c9a5fc8fadb5b741ae4a6d.png#height=585&id=PFSzf&margin=[object Object]&name=image.png&originHeight=585&originWidth=897&originalType=binary&ratio=1&size=74831&status=done&style=none&width=897)

编译成16进制文件后

![image.png](https://img-blog.csdnimg.cn/img_convert/8d8f034b22aa512f44814000715e9cb9.png#height=541&id=gW6Mu&margin=[object Object]&name=image.png&originHeight=541&originWidth=294&originalType=binary&ratio=1&size=54654&status=done&style=none&width=294)

字节码解释

最前面的四个字节码,表示所有文件类型统一的标识符
CA FE BA BE—— 表示这是一个*.class文件
00 00 00 34——00 00是minor version,00 31是major version
00 14——表示常量池中常量的数量,此class文件有24个常量。由于常量池是用4个字节的16进制表示,故一个class文件中,常量数最多为65535。常量池的编号从1开始,0表示不指向任何常量
0a——十进制则表示为10,表示的常量类型为CONSTANT_Methodref_info
00 03——指向常量池的3号内容(名称)
00 11——指向常量池的17号内容(描述符)

字节码顺序

magic(文件格式的标识符)——>minor version(class版本号)——>major version——>constant_pool_count(常量计数器)——>constant_pool(常量)——>access_flags(class名的修饰)——>this_class(本类【指向常量池的引用】)——>super_class(父类【指向常量池的引用】)——>interface_count(实现的接口数)——>interface(实现的接口【指向常量池的引用】)——>fields_count(属性数量)——>fields(具体属性【指向常量池】)——>methods_count(方法数量)——>method_info(具体的方法)——>attribute_count(方法里面代码的数量)——>attribute(具体的代码)

用jclassLib插件解释的class文件

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<>表示这是一个构造方法
<()V> ()表示没有传入参数。V表示返回值是void
在这里插入图片描述

该方法中需要用到的指令

classLoading(class加载器)

jvm强制规定class必须被初始化(加载)的5种情况(只有5种)

  1. 遇到new(创建对象),getstatic(),putstatic()和invokestatic(调用静态方法)这4条指令时,如果类没有初始化时,则需要先触发其初始化。final除外,final的变量,在解析class之前就已经被放到内存中的常量池,不需要加载整个类
  2. 使用java.lang.reflect包的方法对类进行反射调用时候。
  3. 当初始化一个类时,发现其父类没有进行初始化,则需先触发其父类的初始化。
  4. 当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),当前类需要先初始化
  5. 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_putStatic、REF_getStatic、REF_invokeStatic的方法句柄,并且此方法句柄所对应的类没有进行初始化时,需要初始化该类。

加载过程

在这里插入图片描述

class文件在硬盘中——>jvm先把整个文件加载进内存(loading)——>verification(校验加载的class文件的内容是否符合jvm标准)——>preparation(伪静态变量赋默认值而不是代码中设定的初始值,如:static int i=2。此步中,i=0而不是2)——>resolution(class文件中常量的值的富豪替换成内存地址,让jvm可以直接访问)——>initializing(静态变量赋值为初始值)

样例
System.out.println(Integer.class.getClassLoader());
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());

输出

null
sun.misc.Launcher$ExtClassLoader@68f7aae2
Loading

在这里插入图片描述

如果下级加载器(假设为appliactionClassLoader)没找到(没加载)这个class,会先询问父级(ExtensionClassLoader)是否已加载此class,如果还是没找到,再往上级(ClassLoader)找,最终找到父级,还是没找到,最初的子级才会加载此class。如果从子级到父级都没找到此class,则报错ClassNotFound
所有的ClassLoader的最终继承父类都是ClassLoader,故都包含private final ClassLoader parent

public abstract class ClassLoader {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;

    /**
     * Encapsulates the set of parallel capable loader types.
     */
    private static class ParallelLoaders {
        private ParallelLoaders() {}

        // the set of parallel capable loader types
        private static final Set<Class<? extends ClassLoader>> loaderTypes =
            Collections.newSetFromMap(
                new WeakHashMap<Class<? extends ClassLoader>, Boolean>());
        static {
            synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); }
        }

        /**
         * Registers the given class loader type as parallel capabale.
         * Returns {@code true} is successfully registered; {@code false} if
         * loader's super class is not registered.
         */
        static boolean register(Class<? extends ClassLoader> c) {
            synchronized (loaderTypes) {
                if (loaderTypes.contains(c.getSuperclass())) {
                    // register the class loader as parallel capable
                    // if and only if all of its super classes are.
                    // Note: given current classloading sequence, if
                    // the immediate super class is parallel capable,
                    // all the super classes higher up must be too.
                    loaderTypes.add(c);
                    return true;
                } else {
                    return false;
                }
            }
        }

        /**
         * Returns {@code true} if the given class loader type is
         * registered as parallel capable.
         */
        static boolean isRegistered(Class<? extends ClassLoader> c) {
            synchronized (loaderTypes) {
                return loaderTypes.contains(c);
            }
        }
    }

    // Maps class name to the corresponding lock object when the current
    // class loader is parallel capable.
    // Note: VM also uses this field to decide if the current class loader
    // is parallel capable and the appropriate lock object for class loading.
    private final ConcurrentHashMap<String, Object> parallelLockMap;

    // Hashtable that maps packages to certs
    private final Map <String, Certificate[]> package2certs;

    // Shared among all packages with unsigned classes
    private static final Certificate[] nocerts = new Certificate[0];

    // The classes loaded by this class loader. The only purpose of this table
    // is to keep the classes from being GC'ed until the loader is GC'ed.
    private final Vector<Class<?>> classes = new Vector<>();

    // The "default" domain. Set as the default ProtectionDomain on newly
    // created classes.
    private final ProtectionDomain defaultDomain =
        new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
                             null, this, null);

    // Invoked by the VM to record every loaded class with this loader.
    void addClass(Class<?> c) {
        classes.addElement(c);
    }

    // The packages defined in this class loader.  Each package name is mapped
    // to its corresponding Package object.
    // @GuardedBy("itself")
    private final HashMap<String, Package> packages = new HashMap<>();

    private static Void checkCreateClassLoader() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        return null;
    }

    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            assertionLock = new Object();
        } else {
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            assertionLock = this;
        }
    }

    /**
     * Creates a new class loader using the specified parent class loader for
     * delegation.
     *
     * <p> If there is a security manager, its {@link
     * SecurityManager#checkCreateClassLoader()
     * <tt>checkCreateClassLoader</tt>} method is invoked.  This may result in
     * a security exception.  </p>
     *
     * @param  parent
     *         The parent class loader
     *
     * @throws  SecurityException
     *          If a security manager exists and its
     *          <tt>checkCreateClassLoader</tt> method doesn't allow creation
     *          of a new class loader.
     *
     * @since  1.2
     */
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

    /**
     * Creates a new class loader using the <tt>ClassLoader</tt> returned by
     * the method {@link #getSystemClassLoader()
     * <tt>getSystemClassLoader()</tt>} as the parent class loader.
     *
     * <p> If there is a security manager, its {@link
     * SecurityManager#checkCreateClassLoader()
     * <tt>checkCreateClassLoader</tt>} method is invoked.  This may result in
     * a security exception.  </p>
     *
     * @throws  SecurityException
     *          If a security manager exists and its
     *          <tt>checkCreateClassLoader</tt> method doesn't allow creation
     *          of a new class loader.
     */
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

    // -- Class --

    /**
     * Loads the class with the specified <a href="#name">binary name</a>.
     * This method searches for classes in the same manner as the {@link
     * #loadClass(String, boolean)} method.  It is invoked by the Java virtual
     * machine to resolve class references.  Invoking this method is equivalent
     * to invoking {@link #loadClass(String, boolean) <tt>loadClass(name,
     * false)</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 was not found
     */
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    /**
     * 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
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //如果parent不为null
                    if (parent != null) {
                        调用parent参数的loadClass方法
                        c = parent.loadClass(name, false);
                    } else {
                        //如果parent参数为null(证明此ClassLoad是bootstrap),则查找bootstrapClassLoad缓存是否加载过该类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                //如果class还是没加载进来
                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;
        }
    }

    /**
     * Returns the lock object for class loading operations.
     * For backward compatibility, the default implementation of this method
     * behaves as follows. If this ClassLoader object is registered as
     * parallel capable, the method returns a dedicated object associated
     * with the specified class name. Otherwise, the method returns this
     * ClassLoader object.
     *
     * @param  className
     *         The name of the to-be-loaded class
     *
     * @return the lock for class loading operations
     *
     * @throws NullPointerException
     *         If registered as parallel capable and <tt>className</tt> is null
     *
     * @see #loadClass(String, boolean)
     *
     * @since  1.7
     */
    protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) {
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }

    // This method is invoked by the virtual machine to load a class.
    private Class<?> loadClassInternal(String name)
        throws ClassNotFoundException
    {
        // For backward compatibility, explicitly lock on 'this' when
        // the current class loader is not parallel capable.
        if (parallelLockMap == null) {
            synchronized (this) {
                 return loadClass(name);
            }
        } else {
            return loadClass(name);
        }
    }

    // Invoked by the VM after loading class with this loader.
    private void checkPackageAccess(Class<?> cls, ProtectionDomain pd) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            if (ReflectUtil.isNonPublicProxyClass(cls)) {
                for (Class<?> intf: cls.getInterfaces()) {
                    checkPackageAccess(intf, pd);
                }
                return;
            }

            final String name = cls.getName();
            final int i = name.lastIndexOf('.');
            if (i != -1) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        sm.checkPackageAccess(name.substring(0, i));
                        return null;
                    }
                }, new AccessControlContext(new ProtectionDomain[] {pd}));
            }
        }
    }

    /**
     * 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.  This method
     * is deprecated in favor of the version that takes a <a
     * href="#name">binary name</a> as its first argument, and is more secure.
     *
     * @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, or if an attempt is made
     *          to define a class in a package with a fully-qualified name
     *          that starts with "{@code java.}".
     *
     * @see  #loadClass(String, boolean)
     * @see  #resolveClass(Class)
     *
     * @deprecated  Replaced by {@link #defineClass(String, byte[], int, int)
     * defineClass(String, byte[], int, int)}
     */
    @Deprecated
    protected final Class<?> defineClass(byte[] b, int off, int len)
        throws ClassFormatError
    {
        return defineClass(null, b, off, len, null);
    }

    /**
     * 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);
    }

    /* Determine protection domain, and check that:
        - not define java.* class,
        - signer of this class matches signers for the rest of the classes in
          package.
    */
    private ProtectionDomain preDefineClass(String name,
                                            ProtectionDomain pd)
    {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);

        // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
        // relies on the fact that spoofing is impossible if a class has a name
        // of the form "java.*"
        if ((name != null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }

        if (name != null) checkCerts(name, pd.getCodeSource());

        return pd;
    }

    private String defineClassSourceLocation(ProtectionDomain pd)
    {
        CodeSource cs = pd.getCodeSource();
        String source = null;
        if (cs != null && cs.getLocation() != null) {
            source = cs.getLocation().toString();
        }
        return source;
    }

    private void postDefineClass(Class<?> c, ProtectionDomain pd)
    {
        if (pd.getCodeSource() != null) {
            Certificate certs[] = pd.getCodeSource().getCertificates();
            if (certs != null)
                setSigners(c, certs);
        }
    }

    /**
     * Converts an array of bytes into an instance of class <tt>Class</tt>,
     * with an optional <tt>ProtectionDomain</tt>.  If the domain is
     * <tt>null</tt>, then a default domain will be assigned to the class as
     * specified in the documentation for {@link #defineClass(String, byte[],
     * int, int)}.  Before the class can be used it must be resolved.
     *
     * <p> The first class defined in a package determines the exact set of
     * certificates that all subsequent classes defined in that package must
     * contain.  The set of certificates for a class is obtained from the
     * {@link java.security.CodeSource <tt>CodeSource</tt>} within the
     * <tt>ProtectionDomain</tt> of the class.  Any classes added to that
     * package must contain the same set of certificates or a
     * <tt>SecurityException</tt> will be thrown.  Note that if
     * <tt>name</tt> is <tt>null</tt>, this check is not performed.
     * You should always pass in the <a href="#name">binary name</a> of the
     * class you are defining as well as the bytes.  This ensures that the
     * class you are defining is indeed the class you think it is.
     *
     * <p> The specified <tt>name</tt> cannot begin with "<tt>java.</tt>", since
     * all classes in the "<tt>java.*</tt> packages can only be defined by the
     * bootstrap class loader.  If <tt>name</tt> is not <tt>null</tt>, it
     * must be equal to the <a href="#name">binary name</a> of the class
     * specified by the byte array "<tt>b</tt>", otherwise a {@link
     * NoClassDefFoundError <tt>NoClassDefFoundError</tt>} will be thrown. </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
     *
     * @param  protectionDomain
     *         The ProtectionDomain of the class
     *
     * @return  The <tt>Class</tt> object created from the data,
     *          and optional <tt>ProtectionDomain</tt>.
     *
     * @throws  ClassFormatError
     *          If the data did not contain a valid class
     *
     * @throws  NoClassDefFoundError
     *          If <tt>name</tt> is not equal to the <a href="#name">binary
     *          name</a> of the class specified by <tt>b</tt>
     *
     * @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, or if <tt>name</tt> begins with
     *          "<tt>java.</tt>".
     */
    protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

    /**
     * Converts a {@link java.nio.ByteBuffer <tt>ByteBuffer</tt>}
     * into an instance of class <tt>Class</tt>,
     * with an optional <tt>ProtectionDomain</tt>.  If the domain is
     * <tt>null</tt>, then a default domain will be assigned to the class as
     * specified in the documentation for {@link #defineClass(String, byte[],
     * int, int)}.  Before the class can be used it must be resolved.
     *
     * <p>The rules about the first class defined in a package determining the
     * set of certificates for the package, and the restrictions on class names
     * are identical to those specified in the documentation for {@link
     * #defineClass(String, byte[], int, int, ProtectionDomain)}.
     *
     * <p> An invocation of this method of the form
     * <i>cl</i><tt>.defineClass(</tt><i>name</i><tt>,</tt>
     * <i>bBuffer</i><tt>,</tt> <i>pd</i><tt>)</tt> yields exactly the same
     * result as the statements
     *
     *<p> <tt>
     * ...<br>
     * byte[] temp = new byte[bBuffer.{@link
     * java.nio.ByteBuffer#remaining remaining}()];<br>
     *     bBuffer.{@link java.nio.ByteBuffer#get(byte[])
     * get}(temp);<br>
     *     return {@link #defineClass(String, byte[], int, int, ProtectionDomain)
     * cl.defineClass}(name, temp, 0,
     * temp.length, pd);<br>
     * </tt></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 from positions
     *         <tt>b.position()</tt> through <tt>b.position() + b.limit() -1
     *         </tt> should have the format of a valid class file as defined by
     *         <cite>The Java&trade; Virtual Machine Specification</cite>.
     *
     * @param  protectionDomain
     *         The ProtectionDomain of the class, or <tt>null</tt>.
     *
     * @return  The <tt>Class</tt> object created from the data,
     *          and optional <tt>ProtectionDomain</tt>.
     *
     * @throws  ClassFormatError
     *          If the data did not contain a valid class.
     *
     * @throws  NoClassDefFoundError
     *          If <tt>name</tt> is not equal to the <a href="#name">binary
     *          name</a> of the class specified by <tt>b</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, or if <tt>name</tt> begins with
     *          "<tt>java.</tt>".
     *
     * @see      #defineClass(String, byte[], int, int, ProtectionDomain)
     *
     * @since  1.5
     */
    protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        int len = b.remaining();

        // Use byte[] if not a direct ByteBufer:
        if (!b.isDirect()) {
            if (b.hasArray()) {
                return defineClass(name, b.array(),
                                   b.position() + b.arrayOffset(), len,
                                   protectionDomain);
            } else {
                // no array, or read-only array
                byte[] tb = new byte[len];
                b.get(tb);  // get bytes out of byte buffer.
                return defineClass(name, tb, 0, len, protectionDomain);
            }
        }

        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

    private native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd);

    private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd, String source);

    private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
                                         int off, int len, ProtectionDomain pd,
                                         String source);

    // true if the name is null or has the potential to be a valid binary name
    private boolean checkName(String name) {
        if ((name == null) || (name.length() == 0))
            return true;
        if ((name.indexOf('/') != -1)
            || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
            return false;
        return true;
    }

    private void checkCerts(String name, CodeSource cs) {
        int i = name.lastIndexOf('.');
        String pname = (i == -1) ? "" : name.substring(0, i);

        Certificate[] certs = null;
        if (cs != null) {
            certs = cs.getCertificates();
        }
        Certificate[] pcerts = null;
        if (parallelLockMap == null) {
            synchronized (this) {
                pcerts = package2certs.get(pname);
                if (pcerts == null) {
                    package2certs.put(pname, (certs == null? nocerts:certs));
                }
            }
        } else {
            pcerts = ((ConcurrentHashMap<String, Certificate[]>)package2certs).
                putIfAbsent(pname, (certs == null? nocerts:certs));
        }
        if (pcerts != null && !compareCerts(pcerts, certs)) {
            throw new SecurityException("class \""+ name +
                 "\"'s signer information does not match signer information of other classes in the same package");
        }
    }

    /**
     * check to make sure the certs for the new class (certs) are the same as
     * the certs for the first class inserted in the package (pcerts)
     */
    private boolean compareCerts(Certificate[] pcerts,
                                 Certificate[] certs)
    {
        // certs can be null, indicating no certs.
        if ((certs == null) || (certs.length == 0)) {
            return pcerts.length == 0;
        }

        // the length must be the same at this point
        if (certs.length != pcerts.length)
            return false;

        // go through and make sure all the certs in one array
        // are in the other and vice-versa.
        boolean match;
        for (int i = 0; i < certs.length; i++) {
            match = false;
            for (int j = 0; j < pcerts.length; j++) {
                if (certs[i].equals(pcerts[j])) {
                    match = true;
                    break;
                }
            }
            if (!match) return false;
        }

        // now do the same for pcerts
        for (int i = 0; i < pcerts.length; i++) {
            match = false;
            for (int j = 0; j < certs.length; j++) {
                if (pcerts[i].equals(certs[j])) {
                    match = true;
                    break;
                }
            }
            if (!match) return false;
        }

        return true;
    }

    /**
     * Links the specified class.  This (misleadingly named) method may be
     * used by a class loader to link a class.  If the class <tt>c</tt> has
     * already been linked, then this method simply returns. Otherwise, the
     * class is linked as described in the "Execution" chapter of
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @param  c
     *         The class to link
     *
     * @throws  NullPointerException
     *          If <tt>c</tt> is <tt>null</tt>.
     *
     * @see  #defineClass(String, byte[], int, int)
     */
    protected final void resolveClass(Class<?> c) {
        resolveClass0(c);
    }

    private native void resolveClass0(Class<?> c);

    /**
     * Finds a class with the specified <a href="#name">binary name</a>,
     * loading it if necessary.
     *
     * <p> This method loads the class through the system class loader (see
     * {@link #getSystemClassLoader()}).  The <tt>Class</tt> object returned
     * might have more than one <tt>ClassLoader</tt> associated with it.
     * Subclasses of <tt>ClassLoader</tt> need not usually invoke this method,
     * because most class loaders need to override just {@link
     * #findClass(String)}.  </p>
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The <tt>Class</tt> object for the specified <tt>name</tt>
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @see  #ClassLoader(ClassLoader)
     * @see  #getParent()
     */
    protected final Class<?> findSystemClass(String name)
        throws ClassNotFoundException
    {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            if (!checkName(name))
                throw new ClassNotFoundException(name);
            Class<?> cls = findBootstrapClass(name);
            if (cls == null) {
                throw new ClassNotFoundException(name);
            }
            return cls;
        }
        return system.loadClass(name);
    }

    /**
     * Returns a class loaded by the bootstrap class loader;
     * or return null if not found.
     */
    private Class<?> findBootstrapClassOrNull(String name)
    {
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
    }

    // return null if not found
    private native Class<?> findBootstrapClass(String name);

    /**
     * Returns the class with the given <a href="#name">binary name</a> if this
     * loader has been recorded by the Java virtual machine as an initiating
     * loader of a class with that <a href="#name">binary name</a>.  Otherwise
     * <tt>null</tt> is returned.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The <tt>Class</tt> object, or <tt>null</tt> if the class has
     *          not been loaded
     *
     * @since  1.1
     */
    protected final Class<?> findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

    private native final Class<?> findLoadedClass0(String name);

    /**
     * Sets the signers of a class.  This should be invoked after defining a
     * class.
     *
     * @param  c
     *         The <tt>Class</tt> object
     *
     * @param  signers
     *         The signers for the class
     *
     * @since  1.1
     */
    protected final void setSigners(Class<?> c, Object[] signers) {
        c.setSigners(signers);
    }


    // -- Resource --

    /**
     * Finds the resource with the given name.  A resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * <p> The name of a resource is a '<tt>/</tt>'-separated path name that
     * identifies the resource.
     *
     * <p> This method will first search the parent class loader for the
     * resource; if the parent is <tt>null</tt> the path of the class loader
     * built-in to the virtual machine is searched.  That failing, this method
     * will invoke {@link #findResource(String)} to find the resource.  </p>
     *
     * @apiNote When overriding this method it is recommended that an
     * implementation ensures that any delegation is consistent with the {@link
     * #getResources(java.lang.String) getResources(String)} method.
     *
     * @param  name
     *         The resource name
     *
     * @return  A <tt>URL</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found or the invoker
     *          doesn't have adequate  privileges to get the resource.
     *
     * @since  1.1
     */
    public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

    /**
     * Finds all the resources with the given name. A resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * <p>The name of a resource is a <tt>/</tt>-separated path name that
     * identifies the resource.
     *
     * <p> The search order is described in the documentation for {@link
     * #getResource(String)}.  </p>
     *
     * @apiNote When overriding this method it is recommended that an
     * implementation ensures that any delegation is consistent with the {@link
     * #getResource(java.lang.String) getResource(String)} method. This should
     * ensure that the first element returned by the Enumeration's
     * {@code nextElement} method is the same resource that the
     * {@code getResource(String)} method would return.
     *
     * @param  name
     *         The resource name
     *
     * @return  An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
     *          the resource.  If no resources could  be found, the enumeration
     *          will be empty.  Resources that the class loader doesn't have
     *          access to will not be in the enumeration.
     *
     * @throws  IOException
     *          If I/O errors occur
     *
     * @see  #findResources(String)
     *
     * @since  1.2
     */
    public Enumeration<URL> getResources(String name) throws IOException {
        @SuppressWarnings("unchecked")
        Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);

        return new CompoundEnumeration<>(tmp);
    }

    /**
     * Finds the resource with the given name. Class loader implementations
     * should override this method to specify where to find resources.
     *
     * @param  name
     *         The resource name
     *
     * @return  A <tt>URL</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found
     *
     * @since  1.2
     */
    protected URL findResource(String name) {
        return null;
    }

    /**
     * Returns an enumeration of {@link java.net.URL <tt>URL</tt>} objects
     * representing all the resources with the given name. Class loader
     * implementations should override this method to specify where to load
     * resources from.
     *
     * @param  name
     *         The resource name
     *
     * @return  An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
     *          the resources
     *
     * @throws  IOException
     *          If I/O errors occur
     *
     * @since  1.2
     */
    protected Enumeration<URL> findResources(String name) throws IOException {
        return java.util.Collections.emptyEnumeration();
    }

    /**
     * Registers the caller as parallel capable.
     * The registration succeeds if and only if all of the following
     * conditions are met:
     * <ol>
     * <li> no instance of the caller has been created</li>
     * <li> all of the super classes (except class Object) of the caller are
     * registered as parallel capable</li>
     * </ol>
     * <p>Note that once a class loader is registered as parallel capable, there
     * is no way to change it back.</p>
     *
     * @return  true if the caller is successfully registered as
     *          parallel capable and false if otherwise.
     *
     * @since   1.7
     */
    @CallerSensitive
    protected static boolean registerAsParallelCapable() {
        Class<? extends ClassLoader> callerClass =
            Reflection.getCallerClass().asSubclass(ClassLoader.class);
        return ParallelLoaders.register(callerClass);
    }

    /**
     * Find a resource of the specified name from the search path used to load
     * classes.  This method locates the resource through the system class
     * loader (see {@link #getSystemClassLoader()}).
     *
     * @param  name
     *         The resource name
     *
     * @return  A {@link java.net.URL <tt>URL</tt>} object for reading the
     *          resource, or <tt>null</tt> if the resource could not be found
     *
     * @since  1.1
     */
    public static URL getSystemResource(String name) {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            return getBootstrapResource(name);
        }
        return system.getResource(name);
    }

    /**
     * Finds all resources of the specified name from the search path used to
     * load classes.  The resources thus found are returned as an
     * {@link java.util.Enumeration <tt>Enumeration</tt>} of {@link
     * java.net.URL <tt>URL</tt>} objects.
     *
     * <p> The search order is described in the documentation for {@link
     * #getSystemResource(String)}.  </p>
     *
     * @param  name
     *         The resource name
     *
     * @return  An enumeration of resource {@link java.net.URL <tt>URL</tt>}
     *          objects
     *
     * @throws  IOException
     *          If I/O errors occur

     * @since  1.2
     */
    public static Enumeration<URL> getSystemResources(String name)
        throws IOException
    {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            return getBootstrapResources(name);
        }
        return system.getResources(name);
    }

    /**
     * Find resources from the VM's built-in classloader.
     */
    private static URL getBootstrapResource(String name) {
        URLClassPath ucp = getBootstrapClassPath();
        Resource res = ucp.getResource(name);
        return res != null ? res.getURL() : null;
    }

    /**
     * Find resources from the VM's built-in classloader.
     */
    private static Enumeration<URL> getBootstrapResources(String name)
        throws IOException
    {
        final Enumeration<Resource> e =
            getBootstrapClassPath().getResources(name);
        return new Enumeration<URL> () {
            public URL nextElement() {
                return e.nextElement().getURL();
            }
            public boolean hasMoreElements() {
                return e.hasMoreElements();
            }
        };
    }

    // Returns the URLClassPath that is used for finding system resources.
    static URLClassPath getBootstrapClassPath() {
        return sun.misc.Launcher.getBootstrapClassPath();
    }


    /**
     * Returns an input stream for reading the specified resource.
     *
     * <p> The search order is described in the documentation for {@link
     * #getResource(String)}.  </p>
     *
     * @param  name
     *         The resource name
     *
     * @return  An input stream for reading the resource, or <tt>null</tt>
     *          if the resource could not be found
     *
     * @since  1.1
     */
    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * Open for reading, a resource of the specified name from the search path
     * used to load classes.  This method locates the resource through the
     * system class loader (see {@link #getSystemClassLoader()}).
     *
     * @param  name
     *         The resource name
     *
     * @return  An input stream for reading the resource, or <tt>null</tt>
     *          if the resource could not be found
     *
     * @since  1.1
     */
    public static InputStream getSystemResourceAsStream(String name) {
        URL url = getSystemResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }


    // -- Hierarchy --

    /**
     * Returns the parent class loader for delegation. Some implementations may
     * use <tt>null</tt> to represent the bootstrap class loader. This method
     * will return <tt>null</tt> in such implementations if this class loader's
     * parent is the bootstrap class loader.
     *
     * <p> If a security manager is present, and the invoker's class loader is
     * not <tt>null</tt> and is not an ancestor of this class loader, then this
     * method invokes the security manager's {@link
     * SecurityManager#checkPermission(java.security.Permission)
     * <tt>checkPermission</tt>} method with a {@link
     * RuntimePermission#RuntimePermission(String)
     * <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
     * access to the parent class loader is permitted.  If not, a
     * <tt>SecurityException</tt> will be thrown.  </p>
     *
     * @return  The parent <tt>ClassLoader</tt>
     *
     * @throws  SecurityException
     *          If a security manager exists and its <tt>checkPermission</tt>
     *          method doesn't allow access to this class loader's parent class
     *          loader.
     *
     * @since  1.2
     */
    @CallerSensitive
    public final ClassLoader getParent() {
        if (parent == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Check access to the parent class loader
            // If the caller's class loader is same as this class loader,
            // permission check is performed.
            checkClassLoaderPermission(parent, Reflection.getCallerClass());
        }
        return parent;
    }

    /**
     * Returns the system class loader for delegation.  This is the default
     * delegation parent for new <tt>ClassLoader</tt> instances, and is
     * typically the class loader used to start the application.
     *
     * <p> This method is first invoked early in the runtime's startup
     * sequence, at which point it creates the system class loader and sets it
     * as the context class loader of the invoking <tt>Thread</tt>.
     *
     * <p> The default system class loader is an implementation-dependent
     * instance of this class.
     *
     * <p> If the system property "<tt>java.system.class.loader</tt>" is defined
     * when this method is first invoked then the value of that property is
     * taken to be the name of a class that will be returned as the system
     * class loader.  The class is loaded using the default system class loader
     * and must define a public constructor that takes a single parameter of
     * type <tt>ClassLoader</tt> which is used as the delegation parent.  An
     * instance is then created using this constructor with the default system
     * class loader as the parameter.  The resulting class loader is defined
     * to be the system class loader.
     *
     * <p> If a security manager is present, and the invoker's class loader is
     * not <tt>null</tt> and the invoker's class loader is not the same as or
     * an ancestor of the system class loader, then this method invokes the
     * security manager's {@link
     * SecurityManager#checkPermission(java.security.Permission)
     * <tt>checkPermission</tt>} method with a {@link
     * RuntimePermission#RuntimePermission(String)
     * <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
     * access to the system class loader.  If not, a
     * <tt>SecurityException</tt> will be thrown.  </p>
     *
     * @return  The system <tt>ClassLoader</tt> for delegation, or
     *          <tt>null</tt> if none
     *
     * @throws  SecurityException
     *          If a security manager exists and its <tt>checkPermission</tt>
     *          method doesn't allow access to the system class loader.
     *
     * @throws  IllegalStateException
     *          If invoked recursively during the construction of the class
     *          loader specified by the "<tt>java.system.class.loader</tt>"
     *          property.
     *
     * @throws  Error
     *          If the system property "<tt>java.system.class.loader</tt>"
     *          is defined but the named class could not be loaded, the
     *          provider class does not define the required constructor, or an
     *          exception is thrown by that constructor when it is invoked. The
     *          underlying cause of the error can be retrieved via the
     *          {@link Throwable#getCause()} method.
     *
     * @revised  1.4
     */
    @CallerSensitive
    public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }

    private static synchronized void initSystemClassLoader() {
        if (!sclSet) {
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
                Throwable oops = null;
                scl = l.getClassLoader();
                try {
                    scl = AccessController.doPrivileged(
                        new SystemClassLoaderAction(scl));
                } catch (PrivilegedActionException pae) {
                    oops = pae.getCause();
                    if (oops instanceof InvocationTargetException) {
                        oops = oops.getCause();
                    }
                }
                if (oops != null) {
                    if (oops instanceof Error) {
                        throw (Error) oops;
                    } else {
                        // wrap the exception
                        throw new Error(oops);
                    }
                }
            }
            sclSet = true;
        }
    }

    // Returns true if the specified class loader can be found in this class
    // loader's delegation chain.
    boolean isAncestor(ClassLoader cl) {
        ClassLoader acl = this;
        do {
            acl = acl.parent;
            if (cl == acl) {
                return true;
            }
        } while (acl != null);
        return false;
    }

    // Tests if class loader access requires "getClassLoader" permission
    // check.  A class loader 'from' can access class loader 'to' if
    // class loader 'from' is same as class loader 'to' or an ancestor
    // of 'to'.  The class loader in a system domain can access
    // any class loader.
    private static boolean needsClassLoaderPermissionCheck(ClassLoader from,
                                                           ClassLoader to)
    {
        if (from == to)
            return false;

        if (from == null)
            return false;

        return !to.isAncestor(from);
    }

    // Returns the class's class loader, or null if none.
    static ClassLoader getClassLoader(Class<?> caller) {
        // This can be null if the VM is requesting it
        if (caller == null) {
            return null;
        }
        // Circumvent security check since this is package-private
        return caller.getClassLoader0();
    }

    /*
     * Checks RuntimePermission("getClassLoader") permission
     * if caller's class loader is not null and caller's class loader
     * is not the same as or an ancestor of the given cl argument.
     */
    static void checkClassLoaderPermission(ClassLoader cl, Class<?> caller) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // caller can be null if the VM is requesting it
            ClassLoader ccl = getClassLoader(caller);
            if (needsClassLoaderPermissionCheck(ccl, cl)) {
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
        }
    }

    // The class loader for the system
    // @GuardedBy("ClassLoader.class")
    private static ClassLoader scl;

    // Set to true once the system class loader has been set
    // @GuardedBy("ClassLoader.class")
    private static boolean sclSet;


    // -- Package --

    /**
     * Defines a package by name in this <tt>ClassLoader</tt>.  This allows
     * class loaders to define the packages for their classes. Packages must
     * be created before the class is defined, and package names must be
     * unique within a class loader and cannot be redefined or changed once
     * created.
     *
     * @param  name
     *         The package name
     *
     * @param  specTitle
     *         The specification title
     *
     * @param  specVersion
     *         The specification version
     *
     * @param  specVendor
     *         The specification vendor
     *
     * @param  implTitle
     *         The implementation title
     *
     * @param  implVersion
     *         The implementation version
     *
     * @param  implVendor
     *         The implementation vendor
     *
     * @param  sealBase
     *         If not <tt>null</tt>, then this package is sealed with
     *         respect to the given code source {@link java.net.URL
     *         <tt>URL</tt>}  object.  Otherwise, the package is not sealed.
     *
     * @return  The newly defined <tt>Package</tt> object
     *
     * @throws  IllegalArgumentException
     *          If package name duplicates an existing package either in this
     *          class loader or one of its ancestors
     *
     * @since  1.2
     */
    protected Package definePackage(String name, String specTitle,
                                    String specVersion, String specVendor,
                                    String implTitle, String implVersion,
                                    String implVendor, URL sealBase)
        throws IllegalArgumentException
    {
        synchronized (packages) {
            Package pkg = getPackage(name);
            if (pkg != null) {
                throw new IllegalArgumentException(name);
            }
            pkg = new Package(name, specTitle, specVersion, specVendor,
                              implTitle, implVersion, implVendor,
                              sealBase, this);
            packages.put(name, pkg);
            return pkg;
        }
    }

    /**
     * Returns a <tt>Package</tt> that has been defined by this class loader
     * or any of its ancestors.
     *
     * @param  name
     *         The package name
     *
     * @return  The <tt>Package</tt> corresponding to the given name, or
     *          <tt>null</tt> if not found
     *
     * @since  1.2
     */
    protected Package getPackage(String name) {
        Package pkg;
        synchronized (packages) {
            pkg = packages.get(name);
        }
        if (pkg == null) {
            if (parent != null) {
                pkg = parent.getPackage(name);
            } else {
                pkg = Package.getSystemPackage(name);
            }
            if (pkg != null) {
                synchronized (packages) {
                    Package pkg2 = packages.get(name);
                    if (pkg2 == null) {
                        packages.put(name, pkg);
                    } else {
                        pkg = pkg2;
                    }
                }
            }
        }
        return pkg;
    }

    /**
     * Returns all of the <tt>Packages</tt> defined by this class loader and
     * its ancestors.
     *
     * @return  The array of <tt>Package</tt> objects defined by this
     *          <tt>ClassLoader</tt>
     *
     * @since  1.2
     */
    protected Package[] getPackages() {
        Map<String, Package> map;
        synchronized (packages) {
            map = new HashMap<>(packages);
        }
        Package[] pkgs;
        if (parent != null) {
            pkgs = parent.getPackages();
        } else {
            pkgs = Package.getSystemPackages();
        }
        if (pkgs != null) {
            for (int i = 0; i < pkgs.length; i++) {
                String pkgName = pkgs[i].getName();
                if (map.get(pkgName) == null) {
                    map.put(pkgName, pkgs[i]);
                }
            }
        }
        return map.values().toArray(new Package[map.size()]);
    }


    // -- Native library access --

    /**
     * Returns the absolute path name of a native library.  The VM invokes this
     * method to locate the native libraries that belong to classes loaded with
     * this class loader. If this method returns <tt>null</tt>, the VM
     * searches the library along the path specified as the
     * "<tt>java.library.path</tt>" property.
     *
     * @param  libname
     *         The library name
     *
     * @return  The absolute path of the native library
     *
     * @see  System#loadLibrary(String)
     * @see  System#mapLibraryName(String)
     *
     * @since  1.2
     */
    protected String findLibrary(String libname) {
        return null;
    }

    /**
     * The inner class NativeLibrary denotes a loaded native library instance.
     * Every classloader contains a vector of loaded native libraries in the
     * private field <tt>nativeLibraries</tt>.  The native libraries loaded
     * into the system are entered into the <tt>systemNativeLibraries</tt>
     * vector.
     *
     * <p> Every native library requires a particular version of JNI. This is
     * denoted by the private <tt>jniVersion</tt> field.  This field is set by
     * the VM when it loads the library, and used by the VM to pass the correct
     * version of JNI to the native methods.  </p>
     *
     * @see      ClassLoader
     * @since    1.2
     */
    static class NativeLibrary {
        // opaque handle to native library, used in native code.
        long handle;
        // the version of JNI environment the native library requires.
        private int jniVersion;
        // the class from which the library is loaded, also indicates
        // the loader this native library belongs.
        private final Class<?> fromClass;
        // the canonicalized name of the native library.
        // or static library name
        String name;
        // Indicates if the native library is linked into the VM
        boolean isBuiltin;
        // Indicates if the native library is loaded
        boolean loaded;
        native void load(String name, boolean isBuiltin);

        native long find(String name);
        native void unload(String name, boolean isBuiltin);

        public NativeLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
            this.name = name;
            this.fromClass = fromClass;
            this.isBuiltin = isBuiltin;
        }

        protected void finalize() {
            synchronized (loadedLibraryNames) {
                if (fromClass.getClassLoader() != null && loaded) {
                    /* remove the native library name */
                    int size = loadedLibraryNames.size();
                    for (int i = 0; i < size; i++) {
                        if (name.equals(loadedLibraryNames.elementAt(i))) {
                            loadedLibraryNames.removeElementAt(i);
                            break;
                        }
                    }
                    /* unload the library. */
                    ClassLoader.nativeLibraryContext.push(this);
                    try {
                        unload(name, isBuiltin);
                    } finally {
                        ClassLoader.nativeLibraryContext.pop();
                    }
                }
            }
        }
        // Invoked in the VM to determine the context class in
        // JNI_Load/JNI_Unload
        static Class<?> getFromClass() {
            return ClassLoader.nativeLibraryContext.peek().fromClass;
        }
    }

    // All native library names we've loaded.
    private static Vector<String> loadedLibraryNames = new Vector<>();

    // Native libraries belonging to system classes.
    private static Vector<NativeLibrary> systemNativeLibraries
        = new Vector<>();

    // Native libraries associated with the class loader.
    private Vector<NativeLibrary> nativeLibraries = new Vector<>();

    // native libraries being loaded/unloaded.
    private static Stack<NativeLibrary> nativeLibraryContext = new Stack<>();

    // The paths searched for libraries
    private static String usr_paths[];
    private static String sys_paths[];

    private static String[] initializePath(String propname) {
        String ldpath = System.getProperty(propname, "");
        String ps = File.pathSeparator;
        int ldlen = ldpath.length();
        int i, j, n;
        // Count the separators in the path
        i = ldpath.indexOf(ps);
        n = 0;
        while (i >= 0) {
            n++;
            i = ldpath.indexOf(ps, i + 1);
        }

        // allocate the array of paths - n :'s = n + 1 path elements
        String[] paths = new String[n + 1];

        // Fill the array with paths from the ldpath
        n = i = 0;
        j = ldpath.indexOf(ps);
        while (j >= 0) {
            if (j - i > 0) {
                paths[n++] = ldpath.substring(i, j);
            } else if (j - i == 0) {
                paths[n++] = ".";
            }
            i = j + 1;
            j = ldpath.indexOf(ps, i);
        }
        paths[n] = ldpath.substring(i, ldlen);
        return paths;
    }

    // Invoked in the java.lang.Runtime class to implement load and loadLibrary.
    static void loadLibrary(Class<?> fromClass, String name,
                            boolean isAbsolute) {
        ClassLoader loader =
            (fromClass == null) ? null : fromClass.getClassLoader();
        if (sys_paths == null) {
            usr_paths = initializePath("java.library.path");
            sys_paths = initializePath("sun.boot.library.path");
        }
        if (isAbsolute) {
            if (loadLibrary0(fromClass, new File(name))) {
                return;
            }
            throw new UnsatisfiedLinkError("Can't load library: " + name);
        }
        if (loader != null) {
            String libfilename = loader.findLibrary(name);
            if (libfilename != null) {
                File libfile = new File(libfilename);
                if (!libfile.isAbsolute()) {
                    throw new UnsatisfiedLinkError(
    "ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
                }
                if (loadLibrary0(fromClass, libfile)) {
                    return;
                }
                throw new UnsatisfiedLinkError("Can't load " + libfilename);
            }
        }
        for (int i = 0 ; i < sys_paths.length ; i++) {
            File libfile = new File(sys_paths[i], System.mapLibraryName(name));
            if (loadLibrary0(fromClass, libfile)) {
                return;
            }
            libfile = ClassLoaderHelper.mapAlternativeName(libfile);
            if (libfile != null && loadLibrary0(fromClass, libfile)) {
                return;
            }
        }
        if (loader != null) {
            for (int i = 0 ; i < usr_paths.length ; i++) {
                File libfile = new File(usr_paths[i],
                                        System.mapLibraryName(name));
                if (loadLibrary0(fromClass, libfile)) {
                    return;
                }
                libfile = ClassLoaderHelper.mapAlternativeName(libfile);
                if (libfile != null && loadLibrary0(fromClass, libfile)) {
                    return;
                }
            }
        }
        // Oops, it failed
        throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
    }

    private static native String findBuiltinLib(String name);

    private static boolean loadLibrary0(Class<?> fromClass, final File file) {
        // Check to see if we're attempting to access a static library
        String name = findBuiltinLib(file.getName());
        boolean isBuiltin = (name != null);
        if (!isBuiltin) {
            boolean exists = AccessController.doPrivileged(
                new PrivilegedAction<Object>() {
                    public Object run() {
                        return file.exists() ? Boolean.TRUE : null;
                    }})
                != null;
            if (!exists) {
                return false;
            }
            try {
                name = file.getCanonicalPath();
            } catch (IOException e) {
                return false;
            }
        }
        ClassLoader loader =
            (fromClass == null) ? null : fromClass.getClassLoader();
        Vector<NativeLibrary> libs =
            loader != null ? loader.nativeLibraries : systemNativeLibraries;
        synchronized (libs) {
            int size = libs.size();
            for (int i = 0; i < size; i++) {
                NativeLibrary lib = libs.elementAt(i);
                if (name.equals(lib.name)) {
                    return true;
                }
            }

            synchronized (loadedLibraryNames) {
                if (loadedLibraryNames.contains(name)) {
                    throw new UnsatisfiedLinkError
                        ("Native Library " +
                         name +
                         " already loaded in another classloader");
                }
                /* If the library is being loaded (must be by the same thread,
                 * because Runtime.load and Runtime.loadLibrary are
                 * synchronous). The reason is can occur is that the JNI_OnLoad
                 * function can cause another loadLibrary invocation.
                 *
                 * Thus we can use a static stack to hold the list of libraries
                 * we are loading.
                 *
                 * If there is a pending load operation for the library, we
                 * immediately return success; otherwise, we raise
                 * UnsatisfiedLinkError.
                 */
                int n = nativeLibraryContext.size();
                for (int i = 0; i < n; i++) {
                    NativeLibrary lib = nativeLibraryContext.elementAt(i);
                    if (name.equals(lib.name)) {
                        if (loader == lib.fromClass.getClassLoader()) {
                            return true;
                        } else {
                            throw new UnsatisfiedLinkError
                                ("Native Library " +
                                 name +
                                 " is being loaded in another classloader");
                        }
                    }
                }
                NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
                nativeLibraryContext.push(lib);
                try {
                    lib.load(name, isBuiltin);
                } finally {
                    nativeLibraryContext.pop();
                }
                if (lib.loaded) {
                    loadedLibraryNames.addElement(name);
                    libs.addElement(lib);
                    return true;
                }
                return false;
            }
        }
    }

    // Invoked in the VM class linking code.
    static long findNative(ClassLoader loader, String name) {
        Vector<NativeLibrary> libs =
            loader != null ? loader.nativeLibraries : systemNativeLibraries;
        synchronized (libs) {
            int size = libs.size();
            for (int i = 0; i < size; i++) {
                NativeLibrary lib = libs.elementAt(i);
                long entry = lib.find(name);
                if (entry != 0)
                    return entry;
            }
        }
        return 0;
    }


    // -- Assertion management --

    final Object assertionLock;

    // The default toggle for assertion checking.
    // @GuardedBy("assertionLock")
    private boolean defaultAssertionStatus = false;

    // Maps String packageName to Boolean package default assertion status Note
    // that the default package is placed under a null map key.  If this field
    // is null then we are delegating assertion status queries to the VM, i.e.,
    // none of this ClassLoader's assertion status modification methods have
    // been invoked.
    // @GuardedBy("assertionLock")
    private Map<String, Boolean> packageAssertionStatus = null;

    // Maps String fullyQualifiedClassName to Boolean assertionStatus If this
    // field is null then we are delegating assertion status queries to the VM,
    // i.e., none of this ClassLoader's assertion status modification methods
    // have been invoked.
    // @GuardedBy("assertionLock")
    Map<String, Boolean> classAssertionStatus = null;

    /**
     * Sets the default assertion status for this class loader.  This setting
     * determines whether classes loaded by this class loader and initialized
     * in the future will have assertions enabled or disabled by default.
     * This setting may be overridden on a per-package or per-class basis by
     * invoking {@link #setPackageAssertionStatus(String, boolean)} or {@link
     * #setClassAssertionStatus(String, boolean)}.
     *
     * @param  enabled
     *         <tt>true</tt> if classes loaded by this class loader will
     *         henceforth have assertions enabled by default, <tt>false</tt>
     *         if they will have assertions disabled by default.
     *
     * @since  1.4
     */
    public void setDefaultAssertionStatus(boolean enabled) {
        synchronized (assertionLock) {
            if (classAssertionStatus == null)
                initializeJavaAssertionMaps();

            defaultAssertionStatus = enabled;
        }
    }

    /**
     * Sets the package default assertion status for the named package.  The
     * package default assertion status determines the assertion status for
     * classes initialized in the future that belong to the named package or
     * any of its "subpackages".
     *
     * <p> A subpackage of a package named p is any package whose name begins
     * with "<tt>p.</tt>".  For example, <tt>javax.swing.text</tt> is a
     * subpackage of <tt>javax.swing</tt>, and both <tt>java.util</tt> and
     * <tt>java.lang.reflect</tt> are subpackages of <tt>java</tt>.
     *
     * <p> In the event that multiple package defaults apply to a given class,
     * the package default pertaining to the most specific package takes
     * precedence over the others.  For example, if <tt>javax.lang</tt> and
     * <tt>javax.lang.reflect</tt> both have package defaults associated with
     * them, the latter package default applies to classes in
     * <tt>javax.lang.reflect</tt>.
     *
     * <p> Package defaults take precedence over the class loader's default
     * assertion status, and may be overridden on a per-class basis by invoking
     * {@link #setClassAssertionStatus(String, boolean)}.  </p>
     *
     * @param  packageName
     *         The name of the package whose package default assertion status
     *         is to be set. A <tt>null</tt> value indicates the unnamed
     *         package that is "current"
     *         (see section 7.4.2 of
     *         <cite>The Java&trade; Language Specification</cite>.)
     *
     * @param  enabled
     *         <tt>true</tt> if classes loaded by this classloader and
     *         belonging to the named package or any of its subpackages will
     *         have assertions enabled by default, <tt>false</tt> if they will
     *         have assertions disabled by default.
     *
     * @since  1.4
     */
    public void setPackageAssertionStatus(String packageName,
                                          boolean enabled) {
        synchronized (assertionLock) {
            if (packageAssertionStatus == null)
                initializeJavaAssertionMaps();

            packageAssertionStatus.put(packageName, enabled);
        }
    }

    /**
     * Sets the desired assertion status for the named top-level class in this
     * class loader and any nested classes contained therein.  This setting
     * takes precedence over the class loader's default assertion status, and
     * over any applicable per-package default.  This method has no effect if
     * the named class has already been initialized.  (Once a class is
     * initialized, its assertion status cannot change.)
     *
     * <p> If the named class is not a top-level class, this invocation will
     * have no effect on the actual assertion status of any class. </p>
     *
     * @param  className
     *         The fully qualified class name of the top-level class whose
     *         assertion status is to be set.
     *
     * @param  enabled
     *         <tt>true</tt> if the named class is to have assertions
     *         enabled when (and if) it is initialized, <tt>false</tt> if the
     *         class is to have assertions disabled.
     *
     * @since  1.4
     */
    public void setClassAssertionStatus(String className, boolean enabled) {
        synchronized (assertionLock) {
            if (classAssertionStatus == null)
                initializeJavaAssertionMaps();

            classAssertionStatus.put(className, enabled);
        }
    }

    /**
     * Sets the default assertion status for this class loader to
     * <tt>false</tt> and discards any package defaults or class assertion
     * status settings associated with the class loader.  This method is
     * provided so that class loaders can be made to ignore any command line or
     * persistent assertion status settings and "start with a clean slate."
     *
     * @since  1.4
     */
    public void clearAssertionStatus() {
        /*
         * Whether or not "Java assertion maps" are initialized, set
         * them to empty maps, effectively ignoring any present settings.
         */
        synchronized (assertionLock) {
            classAssertionStatus = new HashMap<>();
            packageAssertionStatus = new HashMap<>();
            defaultAssertionStatus = false;
        }
    }

    /**
     * Returns the assertion status that would be assigned to the specified
     * class if it were to be initialized at the time this method is invoked.
     * If the named class has had its assertion status set, the most recent
     * setting will be returned; otherwise, if any package default assertion
     * status pertains to this class, the most recent setting for the most
     * specific pertinent package default assertion status is returned;
     * otherwise, this class loader's default assertion status is returned.
     * </p>
     *
     * @param  className
     *         The fully qualified class name of the class whose desired
     *         assertion status is being queried.
     *
     * @return  The desired assertion status of the specified class.
     *
     * @see  #setClassAssertionStatus(String, boolean)
     * @see  #setPackageAssertionStatus(String, boolean)
     * @see  #setDefaultAssertionStatus(boolean)
     *
     * @since  1.4
     */
    boolean desiredAssertionStatus(String className) {
        synchronized (assertionLock) {
            // assert classAssertionStatus   != null;
            // assert packageAssertionStatus != null;

            // Check for a class entry
            Boolean result = classAssertionStatus.get(className);
            if (result != null)
                return result.booleanValue();

            // Check for most specific package entry
            int dotIndex = className.lastIndexOf(".");
            if (dotIndex < 0) { // default package
                result = packageAssertionStatus.get(null);
                if (result != null)
                    return result.booleanValue();
            }
            while(dotIndex > 0) {
                className = className.substring(0, dotIndex);
                result = packageAssertionStatus.get(className);
                if (result != null)
                    return result.booleanValue();
                dotIndex = className.lastIndexOf(".", dotIndex-1);
            }

            // Return the classloader default
            return defaultAssertionStatus;
        }
    }

    // Set up the assertions with information provided by the VM.
    // Note: Should only be called inside a synchronized block
    private void initializeJavaAssertionMaps() {
        // assert Thread.holdsLock(assertionLock);

        classAssertionStatus = new HashMap<>();
        packageAssertionStatus = new HashMap<>();
        AssertionStatusDirectives directives = retrieveDirectives();

        for(int i = 0; i < directives.classes.length; i++)
            classAssertionStatus.put(directives.classes[i],
                                     directives.classEnabled[i]);

        for(int i = 0; i < directives.packages.length; i++)
            packageAssertionStatus.put(directives.packages[i],
                                       directives.packageEnabled[i]);

        defaultAssertionStatus = directives.deflt;
    }

    // Retrieves the assertion directives from the VM.
    private static native AssertionStatusDirectives retrieveDirectives();
}

class加载的核心

c = parent.loadClass(name, false);

由于parent的不同,所以具体的加载器的loadClass也不一样

手动加载class
ThreadLocalUtil.class.getClassLoader().loadClass("com.me.zookeeper.ZookeeperMainB");
classLoader是java反射的基石
自定义类加载器(重写ClassLoader.findClass()方法)
package com.me;

import com.me.zookeeper.test.Test231;

import java.io.*;

/**
 * 自定义类加载器
 *
 * @author admin
 */
public class CustomClassLoader<T> extends ClassLoader {

    private volatile static int number;

    public CustomClassLoader() {
//        super(new sun.misc.Launcher().getClassLoader());
        super(null);
    }

    public void test() {
        System.out.println("测试加载是否完成");
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        System.out.println(getSystemClassLoader());
        String path = this.getClass().getResource("").getPath();
        File file = new File(path.substring(0, path.indexOf("class") + 8) + name.replaceAll("\\.", "/") + ".class");
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            FileInputStream fileInputStream = new FileInputStream(file);
            int b = 0;
            while ((b = fileInputStream.read()) != -1) {
                byteArrayOutputStream.write(b);
            }
            byteArrayOutputStream.close();
            fileInputStream.close();
            byte[] bytes = byteArrayOutputStream.toByteArray();
            String s = new String(bytes);
            System.out.println(s);
//            return super.defineClass(name.substring(name.lastIndexOf(".")+1), bytes, 0, bytes.length);
            return super.defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    public static void main(String[] args) {
        CustomClassLoader customClassLoader = new CustomClassLoader();
        try {
            Class aClass = customClassLoader.loadClass("com.me.zookeeper.test.Test231");
            System.out.println(aClass.getClassLoader());
            System.out.println(aClass.getName());
            System.out.println(aClass.newInstance());
            Test231 customClassLoader1 = (Test231) aClass.newInstance();
            System.out.println(customClassLoader1);
            customClassLoader1.classLoaderRange();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注:name的名字必须是类名而不能是路径名或者包名

加载过程(双亲委派)

在这里插入图片描述

假设A.class的加载器是CustomClassLoader,CustomClassLoader会在内存中查找是否已加载过此类(此class是否已经存在与内存),如果找到立即返回,没找到则向父级询问,父级查找内存中是否已存在此类,找到后立即返回,没找到再向父级的父级询问,以此类推(虽然类加载器总共才4层)。如果到bootstrap还是没找到,则从当前层级开始尝试加载此class,成功后立刻返回,如果失败则下级尝试加载此类,依次循环。如果到最初发出的类加载器(假设为CustomClassLoader)依然无法加载此类,则抛出异常——ClassNotFound

bootstrap/ext/app类加载器范围

String bootstrapClassLoaderRange = System.getProperty("sun.boot.class.path");
System.out.println("bootstrapClassLeader范围:\n"+bootstrapClassLoaderRange.replaceAll(";", System.lineSeparator()));
String extensionClassLoaderRange = System.getProperty("java.ext.dirs");
System.out.println("extensionClassLeader范围:\n"+extensionClassLoaderRange.replaceAll(";", System.lineSeparator()));
String appClassLoaderRange = System.getProperty("java.class.path");
System.out.println("appClassLoader范围:\n"+appClassLoaderRange.replaceAll(";", System.lineSeparator()));
bootstrapClassLeader范围:
d:\jdk1.8\jre\lib\resources.jar
d:\jdk1.8\jre\lib\rt.jar
d:\jdk1.8\jre\lib\sunrsasign.jar
d:\jdk1.8\jre\lib\jsse.jar
d:\jdk1.8\jre\lib\jce.jar
d:\jdk1.8\jre\lib\charsets.jar
d:\jdk1.8\jre\lib\jfr.jar
d:\jdk1.8\jre\classes
extensionClassLeader范围:
d:\jdk1.8\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
appClassLoader范围:
D:\jdk1.8\jre\lib\charsets.jar
D:\jdk1.8\jre\lib\deploy.jar
D:\jdk1.8\jre\lib\ext\access-bridge-64.jar
D:\jdk1.8\jre\lib\ext\cldrdata.jar
D:\jdk1.8\jre\lib\ext\dnsns.jar
D:\jdk1.8\jre\lib\ext\jaccess.jar
D:\jdk1.8\jre\lib\ext\jfxrt.jar
D:\jdk1.8\jre\lib\ext\localedata.jar
D:\jdk1.8\jre\lib\ext\nashorn.jar
D:\jdk1.8\jre\lib\ext\sunec.jar
D:\jdk1.8\jre\lib\ext\sunjce_provider.jar
D:\jdk1.8\jre\lib\ext\sunmscapi.jar
D:\jdk1.8\jre\lib\ext\sunpkcs11.jar
D:\jdk1.8\jre\lib\ext\zipfs.jar
D:\jdk1.8\jre\lib\javaws.jar
D:\jdk1.8\jre\lib\jce.jar
D:\jdk1.8\jre\lib\jfr.jar
D:\jdk1.8\jre\lib\jfxswt.jar
D:\jdk1.8\jre\lib\jsse.jar
D:\jdk1.8\jre\lib\management-agent.jar
D:\jdk1.8\jre\lib\plugin.jar
D:\jdk1.8\jre\lib\resources.jar
D:\jdk1.8\jre\lib\rt.jar
D:\wok2\zookeeper-test-main\target\test-classes
D:\wok2\zookeeper-test-main\target\classes
D:\maven\org\apache\zookeeper\zookeeper\3.7.0\zookeeper-3.7.0.jar
D:\maven\org\apache\zookeeper\zookeeper-jute\3.7.0\zookeeper-jute-3.7.0.jar
D:\maven\org\apache\yetus\audience-annotations\0.12.0\audience-annotations-0.12.0.jar
D:\maven\io\netty\netty-handler\4.1.59.Final\netty-handler-4.1.59.Final.jar
D:\maven\io\netty\netty-common\4.1.59.Final\netty-common-4.1.59.Final.jar
D:\maven\io\netty\netty-resolver\4.1.59.Final\netty-resolver-4.1.59.Final.jar
D:\maven\io\netty\netty-buffer\4.1.59.Final\netty-buffer-4.1.59.Final.jar
D:\maven\io\netty\netty-transport\4.1.59.Final\netty-transport-4.1.59.Final.jar
D:\maven\io\netty\netty-codec\4.1.59.Final\netty-codec-4.1.59.Final.jar
D:\maven\io\netty\netty-transport-native-epoll\4.1.59.Final\netty-transport-native-epoll-4.1.59.Final.jar
D:\maven\io\netty\netty-transport-native-unix-common\4.1.59.Final\netty-transport-native-unix-common-4.1.59.Final.jar
D:\maven\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar
D:\maven\org\slf4j\slf4j-log4j12\1.7.30\slf4j-log4j12-1.7.30.jar
D:\maven\log4j\log4j\1.2.17\log4j-1.2.17.jar
C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.1\lib\idea_rt.jar
C:\Users\admin\AppData\Local\JetBrains\IntelliJIdea2020.3\captureAgent\debugger-agent.jar
Disconnected from the target VM, address: '127.0.0.1:64870', transport: 'socket'
为什么要搞双亲委派

为了安全
假设有一个场景
你为java.lang.String.class定义了一个自定义类加载器,该类加载器中包含如下功能——>同时使用变量名userName和password的话,向xxx
@xxx.com发送一封邮件,邮件中包含userName和password的值的信息。
如果不做双亲委派,则会出现此类的安全性问题

如何parent(父类加载器)

super(parent)

如何打破双亲委派

重写loadClass()

何时打破过
  1. jdk1.2之前,自定义ClassLoader都必须重写loadClass
  2. ThreadContextClassLoader可以实现基类调用实现类代码,通过thread.setContextClassLoader()指定
  3. 热部署。热启动
    • tomcat、osgi都有自己模块指定clasLoader(可以加在同一类库不同版本)
lazyLoading(懒初始化)

linking

Verification

验证文件是否符合jvm规定

Preparation

静态成员赋值默认值

resolution

将类、方法、属性等符号解析为直接饮用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接饮用

JVM内存屏障规范

LoadLoad屏障

读读屏障(load1|loadload|load2)(load1必须在load2之前运行读取完以下三个类似)

StoreStore屏障

写写屏障(store1|storestore|store2)
在store2写入之前,确保store1的写入操作对其它CPU内核可见

LoadStore屏障

读写屏障(load|loadstore|store)
store写入之前,确保load要读取的数据被读取完毕

StoreLoad屏障

写读屏障(store|storeload|load)(全能屏障)

volatile

Inter使用MESI Cache一致性协议(缓存锁),但不能保证原子性

使用三层内存屏障

需要用到计算机硬件

字节码层

ACC_VOLATILE.

JVM层

StoreStoreBrrier(StoreStore屏障)
volatile写操作
StoreLoadBarrier(StoreLoad屏障)

LoadLoadBrrier(LoadLoad屏障)
volatile读操作
LoadStoreBarrier(LoadStore屏障)

硬件os层

windows 使用lock实现

synchronized实现

使用三层内存屏障

字节码层次

synchroized具体内存指令为
![image.png](https://img-blog.csdnimg.cn/img_convert/85e44e6384aeb43c676af7af3d424fbc.png#height=546&id=CLtQs&margin=[object Object]&name=image.png&originHeight=546&originWidth=633&originalType=binary&ratio=1&size=35933&status=done&style=none&width=633)

monitorenter

进入监控器

monitorexit

监控器退出
一但sync区域内产生异常,立刻调用monitorexit退出sync锁

JVM层

由c、c++调用操作系统实现

硬件层

一堆的lock、lockxchg 、指令

mesi标记数据的四种状态

Modified

Exclusive

Shared

Invalid

Object对象的处理

Object创建过程

  1. class load
  2. class linking(verification,preparation,resolution)
  3. class intializing
  4. 申请对象内存空间
  5. 成员变量赋默认值
  6. 调用构造方法
    1. 成员变量顺序赋初始值
    2. 执行构造方法语句

Object的存储布局

普通对象
  • 对象头 markword 8个字节:Mark Word占用4个字节,Klass Word占用4个字节
  • ClassPointer(指针):-XX:+UseCompressedOops为4字节,不开启为8字节
  • 实例数据
  • padding对齐,8的倍数,每次读取多少个字节
数组对象
  • 对象头 markword 8个字节:Mark Word占用4个字节,Klass Word占用4个字节
  • ClassPointer(指针):-XX:+UseCompressedOops为4字节,不开启为8字节
  • 数组长度:4字节
  • 数组数据
  • padding对齐,8的倍数,每次读取多少个字节

对象头

64位虚拟机
锁状态56bit1bit4bit1bit(是否偏向锁)2bit(锁标志位)
25bit31bit
无锁unused(没用)对象的hashCodeunused对象分代年龄计数器001
偏向锁threadid(54bit)(偏向锁进程id)epoch(2bit)unused对象分代年龄计数器101
轻量级锁指向栈中的记录的指针00
重量级锁指向栈中的记录的指针10
GC标志11
identityHashCode

hashCode默认内容为(没重写):根据该对象内存布局计算出一个hashCode值。

对象在jvm中不是完全连续的,因为存在堆中,还有垃圾回收器的机制影响,总会出现散乱的内存,这就导致了JVM必须为每个对象分配一段内存空间来储存其引用的指针,再结合对象其他必须的元数据,使得对象在持有真实数据的基础上还需要维护额外的数据。转载于阿里云社区

由于对象分代年龄计数器只有4位,所以gc回收的年龄最高只有15(1111)
当一个对象计算过hashcode时,将无法进入偏向锁状态(偏向锁的空间被hashcode占了)

对象定位

假设有个对象——Object o=new Object();

  • 句柄池

o的指针为两部分指针,一个指向实际内存空间,一个指向Object.class
![image.png](https://img-blog.csdnimg.cn/img_convert/c83fa2a4a8b727c404f98a5017e83113.png#height=304&id=AO4gc&margin=[object Object]&name=image.png&originHeight=304&originWidth=372&originalType=binary&ratio=1&size=13524&status=done&style=none&width=372)

  • 直接指针(hotspot使用)

有个指针指向对象的实际内存空间,然后对象的实际内存空间再有个指针指向Object.class
![image.png](https://img-blog.csdnimg.cn/img_convert/4a280f91926bb2fff469813d127a2d26.png#height=315&id=So09k&margin=[object Object]&name=image.png&originHeight=315&originWidth=367&originalType=binary&ratio=1&size=14276&status=done&style=none&width=367)

两种方式的优劣

直接指针的读取效率比句柄池高
触发cms(三色标记)gc时,句柄池的效率比直接指针高

jvm规定重排序必须遵守的规则(happens-before原则)

单一线程原则 Single Thread Role
同一线程内,代码顺序决定执行顺序

管程锁定原则 Monitor Lock Role
对于同一个锁,解锁(UnLock)总是发生在加锁之前(Lock)

volatile变量原则 Volatile Variable Role
对于同一个Volatile变量,写操作总是发生在读操作之前

线程启动原则 Thread Start Role
一个线程的 start()操作,总是发生在这个线程所有动作之前。

线程加入原则 Thread Join Role
Thread 对象的结束先行发生于 join() 方法返回。

线程中断原则 Thread Interrupt Role
对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。

对象终结原则 Finalizer Rule
一个对象的初始化操作总是发生在它的finalize方法之前

传递性原则 Transitivity
如果A先于B,B先于C,那么A先于C。

不管如何重排序,单线程执行结果永远不会改变

jvm1.8运行时的内存区域(内存模型)

主要分为程序计数器(Program Conuter)、堆、本地方法栈、虚拟机栈

线程独享和线程共享

![image.png](https://img-blog.csdnimg.cn/img_convert/fe786d5af090ee99d54604765619a88f.png#height=345&id=CuJr7&margin=[object Object]&name=image.png&originHeight=345&originWidth=457&originalType=binary&ratio=1&size=37097&status=done&style=none&width=457)

方法、栈、线程、栈帧的关系

每个线程单独开辟一个栈
每个方法单独对应一个栈帧

Program Counter(程序计数器)

每个线程都有专门的存放jvm条指令的内存区域。
执行当前指令行数所存放的指令,然后指令行数+1

while(true){
 	取pc中对应的位置,找到对应位置的指令
    执行指令
    执行行数++
}

JVM stacks(虚拟机栈)【vms】

每个线程对应各虚拟机栈,其生命周期与线程共进退,每一个方法被调用时会创建一个帧栈,并入栈,当方法被执行完毕后,出栈。当所有帧栈出栈后,当前线程生命周期结束

栈帧(frame)包含

![image.png](https://img-blog.csdnimg.cn/img_convert/b418dcdd71ec81eb489030d85579291b.png#height=590&id=al0GO&margin=[object Object]&name=image.png&originHeight=590&originWidth=587&originalType=binary&ratio=1&size=52004&status=done&style=none&width=587)
局部变量(Local Variable Table)、动态链接(Dynamic )、操作数栈(operand stack)、方法出口信息(return address)

局部变量

![image.png](https://img-blog.csdnimg.cn/img_convert/c06d524db4b8b7c5d16d75a1bdac9f3a.png#height=627&id=Xwrz7&margin=[object Object]&name=image.png&originHeight=627&originWidth=644&originalType=binary&ratio=1&size=51219&status=done&style=none&width=644)
方法的入参在最前面,方法入参排完后才轮到方法内部的参数
![image.png](https://img-blog.csdnimg.cn/img_convert/d9434124b1cade7fa1ac9d59ff3310e3.png#height=609&id=dRGox&margin=[object Object]&name=image.png&originHeight=609&originWidth=1372&originalType=binary&ratio=1&size=71477&status=done&style=none&width=1372)
非static方法,将自动包含this变量(本对象)在局部变量表的头部

动态链接

链接到局部变量表的某个变量上(或者常量池的某个常量上),查看该变量是否已解析,如果有就直接返回,没有解析则先解析后返回

操作数栈

![image.png](https://img-blog.csdnimg.cn/img_convert/2271b91d215627ed3024b1de4a2235d3.png#height=427&id=mQOU3&margin=[object Object]&name=image.png&originHeight=427&originWidth=607&originalType=binary&ratio=1&size=35312&status=done&style=none&width=607)
处理完成后,return扔到JAVA 机栈栈里面
i=++i
![image.png](https://img-blog.csdnimg.cn/img_convert/be6d7f4e68af130b887dbc063162d68f.png#height=158&id=ZYMmm&margin=[object Object]&name=image.png&originHeight=158&originWidth=297&originalType=binary&ratio=1&size=8608&status=done&style=none&width=297)

指令解释
  1. 将元素入栈
  2. 弹出栈顶元素
  3. 递增
  4. 把int压入栈
  5. 弹出栈顶元素放到新的i里面
  6. 返回

i=i++
![image.png](https://img-blog.csdnimg.cn/img_convert/59bfff6a70ee48026926c6e586d0a963.png#height=139&id=QszTg&margin=[object Object]&name=image.png&originHeight=139&originWidth=301&originalType=binary&ratio=1&size=8228&status=done&style=none&width=301)

  1. 将元素入栈
  2. 弹出栈顶元素
  3. 把int压入栈
  4. 递增
  5. 弹出栈顶元素放到新的i里面
  6. 返回

jvm常用指令(操作数栈指令)

store
load
pop
mul
sub
invoke

invokeStatic

invokeVirtual

invokeInterface

invokeSpecial
可以直接定位,不需要堕胎的方法
例如private方法(siyoufangfa ),构造方法
invokeDynamic
jvm最难的指令
lambda表达式或者反射或者其他动态语言scala kotlin,或者cgLib asm,动态产生的class,会用到的指令

方法出口信息

堆内存模型

堆不分代模型

除了Epsilon(开发调试用的gc)、ZGC、Shenandoah之外的GC都是使用逻辑分代模型

堆分代模型

各个垃圾回收器的对象的年龄限制(年龄大于N就会被移入老年代)
Parallel Scavenge15
GMS6
G115

G1是逻辑分代
除此之外,不仅逻辑分代,而且物理分代

G1垃圾回收器模型

逻辑分代模型的heap(不适用不分代模型的垃圾回收器)

所有线程共用同一个堆。新生代大量死去,少量存活,适用copying算法做gc。存货对象比较多,适用于mark-sweep(标记清除)或者mark-compact(标记压缩)
在这里插入图片描述

内存占据情况

新生代占据1/4,老年代占据3/,比例为1:2

新生代

eden:默认占据新生代空间的8/10。
survivor:有两个区域,每个区域占据1/10。当G1触发垃圾回收时,eden和survivor回收空间

老年代

装待回收的对象。gc.full()触发时,打断所有线程并全量清除老年代的所有数据

查看老年代的占据的比例
java -XX:+PrintFlagsFinal -version|grep NewRatio

在这里插入图片描述

对象生命周期

对象先尝试从stack(栈)上分配,stack分配不了(通常是被引用的对象)就放入eden区;垃圾回收器工作后,仍然存活的对象从eden放入survivor0,并且年龄设置为0;垃圾回收器再次工作时,把survivor0的对象复制到survivor1中,并且把被复制的对象的年龄+1,清空survivor0区域。垃圾回收器再再次工作时,把survivor1的对象复制到survivor0区域,并且把被复制的对象的年龄+1,清空survivor区域。依次循环。。。当对象的年龄大于-XX:MaxTenuringThreshold设置的值(默认为15),复制进老年代等待清除,老年区清除对象会触发fullgc机制,fullgc会造成阻塞,而且老年区的gc效率比年轻代慢10倍以上

CMS 垃圾回收器模型

垃圾回收器ZGC之后不分代,SD不分代

对象分配详解

栈上分配(默认打开)
  • 线程私有小对象(线程变量)
  • 无逃逸。局部变量或者对象,出了指定范围后,该对象(或者变量)失去引用自动失效
  • 支持标量替换。通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配
    • 标量:不可再分解的对象,即基本类型(byte、short、int、long、float、double、char、boolean)。
    • 聚合量:可继续分解的对象。例如复杂对象(String、Object、自定义对象等)
  • 几乎不需要调整
  • 优点
    • 不需要触发gc,只要对象弹出栈顶,即可完成内存区域的回收
线程本地分配TLAB(Thread Local Allocation Buffer)(默认打开)
  • 占用eden,默认1%。默认每个线程占用整个eden区的1%,线程独有的对象分配空间时,从占用的区先分配,不征用其它线程占用的空间
  • 多线程的时候不用金正eden就可以申请空间,提高效率
  • 小对象
  • 几乎不需要调整
老年代
  • 大对象

对象何时进入老年代

超过-XX:MaxTenuringThreshold指定次数
动态年龄

survivor两个区域内(分别)的所有对象的年龄总和加起来超过survivor区内存的一半,则在触发gc后,按照对象的年龄降序(从高到低),把年龄最大的放入老年代。还是超过的话,继续把年龄最大的放入老年代。死循环。

native method stacks(本地方法栈)[nms]

和虚拟机栈一样,区别在于本地方法栈执行的是c/c++编译的方法而不是java方法

本地方法

由其他语言(主要有c/c++)编写,编译和处理相关的方法

direct memory(直接内存)

jvm可以直接访问(读取)的操作系统内核空间的内存,但不能修改,修改部分由操作系统处理。
nio,实现零拷贝(zero copy),提高效率

method area(方法区)MetaSpace(元空间)

所有线程共享。与jvm无关的(非class)的数据(对象)都放在元空间
元空间默认大小为20m,可通过jdk启动参数调整

JVM调优

专业名词

  • MinorGc/YGC:年轻代空间耗尽时触发
  • MajorGC/FullGC:老年代无法继续分配空间时触发,新生代老年代同时进行回收
  • STW:stop-the-world。停止应用的所有工作线程。俗称的停顿时间
  • safe_point:安全地暂停(休眠)线程。例如加锁和解锁。
  • 浮动垃圾:垃圾回收器工作过程中产生的垃圾
  • 根:线程栈变量引用的对象、静态变量引用的对象、常量池、jin指针(c/c++的指针)
  • fgc:full gc。jvm的老年代剩余空间不足,需要清除老年代的所有对象回收空间。此时引用所有线程都会停止,等待fgc完事后才继续工作。PO、CMS、SO只会在FGC触发后才会运行,也可以这么认为——PO、CMS、SO就是FGC
  • OutOfMemoryError(内存溢出):堆剩余空间不足以放对象,则会报错OutOfMemoryError
  • 内存泄漏:触发gc的时候没有回收(清除)需要回收的对象
  • 标量:最下级不可分割的变量。也就是说,非基本数据类型的对象可以不断拆解,直至拆解成个基本数据类型后无法继续拆解。基本数据类型就是标量
  • 标量替换:把基本数据类型恢复成普通的复杂对象。这就是标量替换

HotSpot参数分类

  • 标准:-开头,所有HotSpot都支持
  • 非标准: -X开头,特定版本HotSpot支持
  • 不稳定:-XX开头,下个版本可能取消

例子

java -version
java -X

查看jvm配置信息

java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=257905728 -XX:MaxHeapSize=4126491648 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

参数解释

InitialHeapSize默认堆大小(单位为B)——Xms
MaxHeapSize最大堆大小(单位为B)——Xmx
UseCompressedClassPointers对象头
UseCompressedOops对象内存布局
UseLargePagesIndividualAllocation
UseParallelGCGC垃圾回收
UseCompressedOops是否开启指针压缩(64位系统+内存大于4G且小于32G,默认开启指针压缩)

查看jvm大概的参数说明

java

![image.png](https://img-blog.csdnimg.cn/img_convert/56ca5743321151d2ec7b3a8f456190c8.png#height=827&id=RtlUU&margin=[object Object]&name=image.png&originHeight=827&originWidth=748&originalType=binary&ratio=1&size=66248&status=done&style=none&width=748)

查看jvm完整参数

java -XX:+PrintCommandLineFlags

![image.png](https://img-blog.csdnimg.cn/img_convert/cda8213cc31ad3c2b8eb10b4e890d87a.png#height=477&id=XEyyi&margin=[object Object]&name=image.png&originHeight=477&originWidth=961&originalType=binary&ratio=1&size=47070&status=done&style=none&width=961)

根据关键字搜索jvm参数

java -XX:+PrintFlagsFinal -version |grep Use

查看 GC运行参数

java -Xmn10M -Xms40M -Xmx40M -XX:+PrintCommandLineFlags -XX:+PrintGC Test PrintGCDetails PrintGCTimeSTamps PrintGCCauses
参数介绍
-Xmn10m年轻代大小为10m
-Xmx40M堆最大为40m
-Xms40m堆最小为40m
+PrintCommandLineFlags打印信息
+PrintGC打印GC信息
PrintGCDetails打印详细的GC信息
PrintGCTimeSTamps打印产生gc的系统时间戳
PrintGCCauses打印产生GC的原因

![image.png](https://img-blog.csdnimg.cn/img_convert/e4609464231282f43f77e93fc9de00ad.png#height=53&id=AmNel&margin=[object Object]&name=image.png&originHeight=53&originWidth=963&originalType=binary&ratio=1&size=8091&status=done&style=none&width=963)
![image.png](https://img-blog.csdnimg.cn/img_convert/d426764b4b1f07c1ba8da1b5accfdf17.png#height=40&id=QJhcT&margin=[object Object]&name=image.png&originHeight=40&originWidth=485&originalType=binary&ratio=1&size=3876&status=done&style=none&width=485)

参数解释
-XX:InitialHeapSize=41943040初始堆大小为41943040字节,即40m
-XX:MaxHeapSize=41943040最大堆大小为41943040字节,即40m
-XX:MaxNewSize=10485760最大年轻代大小为10485760字节,即10m
-XX:NewSize=10485760年轻代初始大小为10485760字节,即10m
GC/Full GC产生的GC类型,是普通GC还是Full GC
(System.gc())产生gc的原因
880K->620K(39936K)年轻代/老年代,内存占用从880K到620K
0.0030584 secs总共花了多长时间
PS垃圾回收器日志详解
![image.png](https://img-blog.csdnimg.cn/img_convert/c9c8b9eebec02c9221d0a21e447bc27f.png#height=417&id=DSWKH&margin=[object Object]&name=image.png&originHeight=417&originWidth=839&originalType=binary&ratio=1&size=566393&status=done&style=none&width=839)

相关调优参数

GC通用参数

-XX:MaxTenuringThresholdsurvivor-0到survivor-1之间复制的年龄超过限制,进入老年代。由于jmm中,对象头的年龄为4位二进制,故无论如何修改,年龄最大为15.
-XX:+DoEscapeAnalysis开启逃逸分析
-XX:-DoEscapeAnalysis关闭逃逸分析
-Xms15m设置最小堆内存为15m
-Xmx15m设置最大堆内存为15m
-Xmn5m设置jvm年轻代大小为5m
-Xss15m设置每个线程的堆栈的大小为15m。每个线程都有自己独立的堆栈,不设置则默认为1m
-XX:SurvivorRatio=4年轻代中eden与两个survivor比例,eden占据4/10
-XX:+PrintGC打印gc日志
-XX:-UseTLAB关闭TLAB
-XX:+PrintTLAB打印TLAB(逃逸分析)使用情况
-XX:+EliminateAllocations启动标量替换。默认为启动状态
-XX:-EliminateAllocations关闭标量替换
-Xmixed混合模式(默认)
-Xint仅解释模式执行
-Xnoclassgc禁用GC
-Xincgc启用增量垃圾收集
-Xloggc:将 GC 状态记录在文件中(带时间戳)
-Xbatch禁用后台编译
-XX:+PrintGCApplicationConcurrentTime打印应用程序时间
-XX:+PrintGCApplicationStoppedTime打印暂停时长
-Xfuture启用最严格的class文件检查。默认不启用
-Xrs减少 Java/VM 对操作系统信号的使用
-Xshare:off不尝试使用共享类数据
-Xshare:auto在可能的情况下使用共享类数据。默认开启
-Xshare:on要求使用共享类数据,否则将失败
-XshowSettings:system(仅限 Linux)显示系统或容器置并继续运行
-XshowSettings:all显示所有配置并继续
-XshowSettings:vm显示所有与 vm 相关的设置并继续
-XshowSettings:properties显示所有属性设置并继续
-XshowSettings:locale显示所有与区域设置相关的设置并继续
-XX:+UseCMSCompactAtFullCollectioncms开标记压缩。默认为true。-UseCMSCompactAtFullCollection则为不开启标记压缩
-XX:CMSFullGCsBeforeCompaction=0CMS 执行0次foreground GC后才执行1次MSC算法压缩堆内存。
-XX:CMSInitiatingOccupancyFraction=50CMS的老年代空间占用超过50%后就立刻执行一次CMS GC,确保CMS老年代的剩余空间足够产生浮动垃圾
-XX:+UseCMSCompactAtFullCollection开启CMS的full.gc(),启动CMS的标记压缩。此参数默认为true
-XX:+UseGCLogFileRotation记录GC日志。
该参数有个缺点:假设GC日志文件总数为,gc日志名分别为GC.1.LOG、GC.2.LOG。。。时,当GC.5.LOG文件大小到达上限后,gc日志会把GC.1.LOG数据清空并重新写入日志信息。
-XX:+PrintReferenceGC记录回收了多少种不同引用类型的引用
-verbose类加载详细过程
-XX:+PrintFlagsFinal以命令行的方式输出jvm日志信息
-XX:+PrintFlagsInitial查看所有JVM参数启动的初始值
-XX:MetaspaceSize=256m元空间最小大小为256m
-XX:MetaspaceSize=256m元空间最大大小为256m

Parallel常用参数

-XX:PreTenureSizeThreshold=0大对象的大小,单位字节,默认为0,就是说任何对象都会在年轻代分配内幕才能
-XX:MaxTenuringThreshold=3年轻代内的对象晋升到老年代的年龄限制,默认15.
-XX:+ParallelGCThreads并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
-XX:+UseAdaptiveSizePolicy自动选择各区大小比例

CMS常用参数

-XX:+UseConcMarkSweepGC开启CMS老年代的并行垃圾回收器
-XX:ParallelCMSThreads
-XX:CMSInitiatingOccupancyFaction指定老年代的控件使用率,当老年代的空间占用率在CMSInitiatingOccupancyFaction值之上时,执行CMS垃圾回收。默认68
-XX:+UseCMSCompactAtFullCollectionCMS垃圾回收之后重新整理内存空间(标记压缩)
-XX:CMSFullGCsBeforeCompactionCMS执行几次之后才会进行标记压缩
-XX:+CMSClassUnloadingEnabled:允许对类元数据区进行回收
-XX:+CMSInitiatingPermOccupancyFaction当永久代占用到100%时,启动CMS回收(前提是CMSClassUnloadingEnabled开启了)
-XX:CMSInitiatingOccupancyOnly只有达到阈值时才进行CMS回收
-XX:CMSInitiatingPermOccupancyFraction达到什么比例时进行Pern回收
-XX:GCTimeRatio设置GC时间占用程序运行时间的百分比
-XX:MaxGCPauseMillis停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代

G1常用参数

-XX:+UseG1GC使用G1垃圾回收器
-XX:MaxGCPauseMillis=2G1垃圾回收时会不断调整年轻代尝试达到该毫秒数
-XX:GCPauseIntervalMillisGC的运行间隔理想值
-XX:+G1HeapRegionSize分区大小,建议逐渐增大该值,1 2 4 8 16 32。 随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长
-XX:G1NewSizePercent=3新生代最小比例,默认为5%
-XX:GCTimeRatioGC时间建议比例,G1会根据这个值调整堆空间
-XX:ConcGCThreadsG1启动的线程数量
-XX:InitiatingHeapOccupancyPercent启动G1的堆空间占用比例

GC(垃圾回收)

什么时候会产生full.gc()

  1. 老年区可用空间满了
  2. 元空间(nnamespace)扩容

如何定位垃圾

reference count(引用计数器)

在每个对象的字节码中,加上引用该对象的次数(reference count),每当有一个变量(或对象)引用该对象,则在计数器上+1。当该变量不引用该对象时,引用计数器-1,当引用计数器等于0时,该对象被标记为等待回收,gc运行是可能会清除。没有清除该对象时,该对象的年龄+1,当年龄>15,则放入老年代,下次出发gc full clean时将会阻塞非gc线程,清除老年代的所有空间,清除完后,唤醒所有线程。

int i=0;
j=i;
k=i;

代码执行到第一行,创建了i变量,i指向的引用对象为"0",并且"0"的引用数量为1。执行到第二行,""0"的引用数量为2。执行到第三行,"0"的引用数量为3。代码执行完了,gc开始干活,k被回收,"0"的引用数量为2,j被回收,"0"的引用数量为1,被回收,引用对象0的引用数量为0。此时,gc检测到0的引用数量为0,应该被回收,所以,"0"也被回收了.
注意:reference count不能解决循环引用
![image.png](https://img-blog.csdnimg.cn/img_convert/4684cbad33e1b806994b2e16e3431cd3.png#height=233&id=eqk2o&margin=[object Object]&name=image.png&originHeight=233&originWidth=338&originalType=binary&ratio=1&size=11242&status=done&style=none&width=338)

root searching(根可达算法)——解决循环引用垃圾回收问题

该变量(变量不是根对象)不可溯源到根对象,则标记为垃圾,等待清除

根对象包括

线程栈变量(普通变量 )、静态变量、常量池、jni指针(c、c++的指针)

常用的垃圾回收器

在这里插入图片描述

分代

在这里插入图片描述

jvm最早诞生的垃圾回收器是serial,之后诞生了Parallel-Scavenge(ps),之后诞生了cms,而为了配合cms,在ps的基础上改版,催生出ParalleNew(pn)

  • Serial
    • 最早出现的垃圾回收器
    • 串行回收。单线程清理对象回收空间。当此GC干活的时候,应用的所有线程都被打断,直到此GC工作完成后才唤醒工作线程.。单核CPU工作效率最高,是jvm的client模式默认的垃圾回收器
  • Parallel Scavenge
    • 年轻代
    • 并行回收。多线程清理对象回收空间。仍然会打断应用的工作线程
  • ParNew
    • 年轻代
    • 并行回收
    • 与ps大致一样,为了与CMS配合,做了某些修改。
  • SerialOld
  • ParalleOld
  • ConcurrentMarkSweep(CMS)
    • 老年代
    • 并发回收的GC。 垃圾回收和应用程序同时运行,
    • 并发垃圾回收是因为无法忍受stw
    • 由于CMS的问题较多,目前没有一个版本默认的GC是CMS,只能手工指定
    • CMS有碎片化的问题,碎片到达一定程度,CMS的老年代空间不足的时候,使用SeriaOld进行老年代的空间回收
    • CMS应对大内存块的时候,CMS的FGC效率反而会更低(CMS线程执行时间更长,应用工作线程唤醒时间延迟更高)
    • 缺点
      • 碎片过多会导致需要传入老年代对象无法扩大,触发fgc(full gc),stw最长有十几小时到几天时间。解决方式
        • 加入运行参数——-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0(开启标记压缩,foreground gc后立刻开始标记压缩)
      • CMS的老年代空间不足的时候(年轻代的对象不能往老年代里装),使用SerialOld进行老年代的空间回收,由SerialOld(FGC)进行单线程的标记压缩。解决方案
        • 在CMS老年代空间占用超过阈值后自动执行CMS GC,让CMS有足够的空间产生浮动垃圾。命令为——-XX:CMSInitiatingOccupancyFraction=50,50为CMS老年代空间占用百分比,可以调得更低
      • 会产生浮动垃圾。即清除的过程中cms本身会产生新的垃圾,导致CMS的老年代空间不足。
  • G1(10ms)
不分代
  • ZGC(实际stw在5ms以内)
  • Shenandoah
DEBUG开发使用
  • eplison

JDK1.8默认的垃圾回收:Ps+paralleOld

常用的GC组合
  • Serial+SerialOld
  • CMS+ParNew
  • ParallelScavenge+ParallelOld。默认的GC

垃圾收集器(GC)适用内存大小

  • serial 几十M
  • PS 一百到几个G
  • CMS 几个G到20G
  • G1 上百G
  • ZGC 4T

CMS

CMS只工作与old区,也就是只有触发FGC才会使用CMS
CMS gc 有两种模式,background和foreground,正常的cms gc使用background模式,就是我们平时说的cms gc;当并发收集失败或者调用了System.gc()的时候,就会导致一次full gc,这个fullgc是不是cms回收,而是Serial单线程回收器,加入了参数[12]后,执行full gc的时候,就变成了CMS foreground gc,它是并行full gc,只会执行cms中stop the world阶段的操作,效率比单线程Serial full GC要高。

从线程开始理解CMS的阶段

在这里插入图片描述

初始标记

单线程寻找根可以关联的对象

并发标记

在工作线程运行的同时,采用多线程标记出根关联对象(不需要清理的对象)

重新标记

由于工作线程正在运行,并发标记中可能会产生新的垃圾,或者旧有的垃圾被工作线程重新使用(也就是说该对象不是垃圾),重新执行并发标记的过程;但,重新标记是stw,工作线程停止运行。

并发清理

基于标记结果,把没有标记的全部清掉。清理过程中会产生浮动垃圾,等待下次cms的gc运行

三色标记

假设有三种颜色,分别标记为不同的对象状态,白色、灰色、黑色。最开始所有对象都是白色,然后把其中的全局变量和栈帧里的局部对象(或变量)设置为灰色,然后把灰色的设置为黑色,然后把原来的灰色对象指向的变量设置为灰色,以此类推。当发现没有对象可设置为灰色时,剩下的白色的对象(或者变量)就一定是需要被清除的垃圾

GC常用回收算法

mark-sweep(标记清除)

把需要清除的对象都标记好,gc触发的时候清除标记对象回收内存空间。

优点
  1. 算法相对简单
  2. 存活对象比较多,需要清理的对象数量比较少,效率较高。也就是说,由于eden存活数量较少,不适合使用标记清除算法
缺点
  1. 需要两次扫描,执行效率偏低。第一次扫描标记出没用的,第二次扫描找出没用的并清除
  2. 容易产生碎片。mark-sweep(标记清除)清除标记的对象后,会在原地留出一堆空洞(碎片),mark-sweep并不会把留下来的数据重新排序把窟窿填上。由此,内存空间的窟窿(碎片)越来越多,越来越影响效率
copying(复制)

把有用的对象复制到另一个块内存区域,原有的空间全部清除。适合eden区。

优点
  1. 适用于存活数量较少
  2. 只扫描一次,不需要清除的对象直接复制到另一个空间,不会产生碎片
缺点
  1. 空间浪费,gc期间可使用内存减半
  2. 移动和复制对象,需要重新调整引用。
mark-compact(标记压缩)

把有用的对象全往前面压
在这里插入图片描述

优点
  1. 不会产生碎片(空洞),gc期间也不会造成可使用内存减半的后果
缺点
  1. 需要扫描两次,第一次标记出待清除的对象,第二次清除对象
  2. 效率较低。一边清除垃圾,一边移动有用的对象到空位上,所以效率较低。

GC注意事项

CMS并发GC不是“full GC”。HotSpot VM里对concurrent collection和full collection有明确的区分。所有带有“FullCollection”字样的VM参数都是跟真正的full GC相关,而跟CMS并发GC无关的

JVM调优真实做法

调优前的注意事项

  • 吞吐量:用户代码时间/(用户代码执行时间+垃圾回收时间)
    • 吞吐量=并发量
  • 响应时间:STW越短,响应时间越好
    • 响应时间=进入处理后给到返回数据之间的时间

gc回收并行回收和串行回收

并行回收

只有一条线程进行垃圾回收。应用的吞吐量较大,但停顿时间较长。

并发回收

多条线程进行垃圾回收。应用的吞吐量较小,停顿时间也较少

调优,从规划开始

  • 调优从业务场景开始,没有场景不调优
  • 无监控(压力测试,能看到结果)不调优

基本步骤

  1. 选择合适的GC
    1. 响应时间、停顿时间(ZGC、G1、CMS)(需要给用户作响应)。
    2. 吞吐量:选择PS
  2. 计算内存需求
  3. 选定CPU,频率越高越好。cpu越高,gc的效率越高
  4. 设定年轻代大小,升级年龄
  5. 打印日志
    • -Xloggc:/logs/gc-%t.log -XX:+UseGCLogFileRotation XX:NumberOfGCLogFiles=5 -XX:GcLogFileSize=20m -XX:PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
      • -XX:+PrintGC 输出GC日志
      • -XX:+PrintGCDetails 输出GC的详细日志
      • -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
      • -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
      • -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
      • -Xlogg/logs/gc-%t.log 日志文件的输出路径。%t=系统时间
      • -XX:GCLogFileSize=20M(k) 日志文件大小为20m(k)
      • -XX:+PrintGCCause GC产生的原因
      • XX:NumberOfGCLogFiles 日志文件的数量,值必须>1
  • 查看日志

详细步骤

  1. 收到报警信息(cpu报警)
    1. 使用top命令打印出进程使用资源情况,找出占用cpu/内存最高的进程
    2. 使用 top -Hp [进程号] 查看进程内的线程。
    3. 进入到相应的使用者下执行命令jstack [进程号] 查看线程状态,输出格式为这样

![image.png](https://img-blog.csdnimg.cn/img_convert/3b27c9540ca076fd065ec42d30b203d8.png#height=454&id=G5SmA&margin=[object Object]&name=image.png&originHeight=454&originWidth=968&originalType=binary&ratio=1&size=75331&status=done&style=none&width=968)

  1. 重点关注状态为WAITING、BLOCKED的线程。Waiting是等待锁的线程,等待锁有可能是死锁。Bolcked是被打断的线程。以下的t2就是等待锁的现场

![image.png](https://img-blog.csdnimg.cn/img_convert/f2fdfced411681a47ce92930108168a9.png#height=243&id=m5qpu&margin=[object Object]&name=image.png&originHeight=243&originWidth=1474&originalType=binary&ratio=1&size=175093&status=done&style=none&width=1474)

  1. 重点关注waiting on <0x000324dfg> (a java.lang.Object)。这是watting状态的线程正在等待该对象的资源释放以争取资源。查找哪个线程持有该对象。最简单的办法,由于jstack打印出了该进程内所有的线程情况,所以直接在xshell内搜索该对象的内存地址,找到有的对象并且状态为runnable的线程即可。
  2. jinfo [进程号] 查看jvm详细信息。帮助不大
  3. 内存和cpu同时报警
  4. 可能是应用oom(内存溢出),从而频繁fullgc,但每次回收的内存空间都不够多,导致执行完fullgc后立刻开启下一次的fullgc
  5. jstat -gc 动态观察gc情况/月度GC日志发现活动频繁的GC。
jstat -gc [进程号] 500 每隔500毫秒打印一次gc情况
  1. 定位oom问题
  2. 使用监控工具检查jvm进程

在这里插入图片描述

  1. 选择抽样器,根据实例数量和大小判断。能最简单判断oom的对象。
  1. 但是这样有个问题——用jmx工具监控java应用的话,java应用需要额外消耗性能
  1. 使用[arthas](https://www.yuque.com/docs/share/9d6f0ee9-b265-4ad0-8ab4-ea9d0ba6804d?# 《arthas介绍以及使用》)定位问题
  2. 启动应用的时候加上如下命令
-XX:HeadpDumpOnOutMemoryError
# 完整命令为
java -Xms20m -Xmx20m -XX:HeadpDumpOnOutMemoryError -XX:PrintGC xxx.jar
  1. jmap -histo [进程号] |head -[显示行数]
jmap -histo 4869|head -20
  1. 在这里插入图片描述

  2. 如果是线上系统,内存特别大,jmap执行时间会对进程产生影响,甚至卡顿。执行期间可能进程瘫痪。解决方案

    1. 启动的时候设定参数HeapDump,OOM(内存溢出,频繁gc)的时候自动产生堆转储文件
    2. 服务做高可用,停掉单个服务对整个后台系统影响不大
    3. 在限定为arthas
  3. jmap -dump:format=b,file xxx pid / jmap-histo。手动导出堆转储文件。线上环境内存较大的时候不推荐使用

堆基本设置

  • -Xmx与Xms,最大堆大小与最小堆大小最好设置为一样,避免jvm频繁弹调整堆的实际大小
    • 当Xms比Xmx小的时候,jvm会根据堆的占用大小时刻调整占用内存大小,浪费宝贵的cpu资源

G1调优

g1只有逻辑分代,没有物理分代。

监控插件

jconsole

一般用于上线之前的测试
需要监控的java应用启动时加入如下参数

-Dcom.sun.management.jmxremote.port=8999 #对外暴露的端口
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=192.168.1.1    #ip地址白名单

jmxremote.password.template文件复制,改名成jmxremote.password,设置权限

window设置权限

在这里插入图片描述

window版jconsole位置

{jdk}/bin/jconsole.exe
在这里插入图片描述

jVisualVm

一般用于上线之前的测试

window版位置{jdk}/bin/jvisualvm.exe

在这里插入图片描述

查看正在运行的应用的内存信息

在这里插入图片描述

arthas

优化场景

有一个网站,用户浏览频繁,服务器卡顿。升级内存(仅升级内存)后更卡
原先卡顿的原因
  • 用户频繁访问,但内存不足,频繁gc。swt导致响应时间变慢

升级后更卡顿的原因

  • 内存越大,FGC时间更长
优化方法
  • PS换成PN+CMS或者G1
CPU占用经常100%,如何调优
  • 找出进程占用率最高
  • 找出进程中的线程占用最高
  • 如果是java的话,查找线程中的堆栈(jstack)
  • 查找哪个方法(栈帧)的消耗时间最长(jstack)
内存飙高,如何查找问题
  • 导出堆内存(jmap)
  • 分析
内存占用飙高,如何调优
垂直电商,每日并发量10万订单,需要什么配置
  • 找并发量高发期的时间段
  • 计算:一个订单从生产到响应,需要多少内存
  • 专业问法:要求100ms响应时间
  • 解决方案,通过压测不断调整参数

12306遭遇春运并发量骤增

  • 下单->减库存和生成订单都做异步处理->用户付款,异步通知消息->修改订单状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值