HotSpot启动的过程中,在init_globals()函数中universe2_init()函数中已经预加载了系统运行必须的Java类。所以我们先分析预加载的系统运行必须的类,然后再看JVM启动以后我们的Hello类是如何加载的。JVM的组成如下

1. 方法区:保存Java类元数据,线程共享的运行时内存区域
2. 堆:所有类实例和数组对象分配内存的区域
3. Java栈:每个线程私有,与传统c栈类似,存储局部变量,方法栈帧等
4. pc寄存器:每个线程私有,程序运行计数指针
5. 本地方法栈:传统c栈
6. 类加载器:加载Java字节码
7. 执行引擎:字节码解释器/模板解释器

虚拟机启动以后会加载指定的初始化主类,并执行主类包含的main方法,主类运行结束以后虚拟机将会退出,一般服务器tomcat会有一个主线循环让虚拟机不退出。Java程序在虚拟机运行的第一步就是类加载。

一. HotSpot内部预加载类

hotspot/src/share/vm/memory/universe.cpp

void universe2_init() {......Universe::genesis(CATCH);
}

hotspot/src/share/vm/memory/universe.cpp

void Universe::genesis(TRAPS) {//首先是基本型的初始化.....//初始化符号表vmSymbols::initialize(CHECK);//初始化系统字典SystemDictionary::initialize(CHECK);
}

hotspot/src/share/vm/classfile/vmSymbols.cpp


//注意这里的 VM_SYMBOLS_DO 符号
#define VM_SYMBOL_BODY(name, string) string "\0"
static const char* vm_symbol_bodies = VM_SYMBOLS_DO(VM_SYMBOL_BODY, VM_ALIAS_IGNORE);void vmSymbols::initialize(TRAPS) {if (!UseSharedSpaces) {//vm_symbol_bodies声明在上面const char* string = &vm_symbol_bodies[0]; for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) {//为Java类创建符号Symbol* sym = SymbolTable::new_permanent_symbol(string, CHECK);//存到符号数组中_symbols[index] = sym;string += strlen(string); // skip string body//下一个string += 1;              // skip trailing null}//Java基本类型_type_signatures[T_BYTE]    = byte_signature();.....// no single signatures for T_OBJECT or T_ARRAY}
}

hotspot/src/share/vm/classfile/vmSymbols.hpp
先来看看 VM_SYMBOLS_DO,它在头文件中是个宏定义

#define VM_SYMBOLS_DO(template, do_alias)                                                         \/* commonly used class, package, module names */                                                \template(java_base,                                 JAVA_BASE_NAME)                             \template(java_lang_System,                          "java/lang/System")                         \template(java_lang_Object,                          "java/lang/Object")                         \template(java_lang_Class,                           "java/lang/Class")                          \template(java_lang_Package,                         "java/lang/Package")                        \template(java_lang_Module,                          "java/lang/Module")                         \......    

hotspot/src/share/vm/classfile/symbolTable.cpp
再来看看 new_permanent_symbol,符号表维护着Java类/字符等信息

Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) {unsigned int hash;//从符号表中查找符号应用,SymbolTable是HashTableSymbol* result = SymbolTable::lookup_only((char*)name, (int)strlen(name), hash);if (result != NULL) {return result;}//如果不存在则创建hash索引,并放到表中SymbolTable* table = the_table();int index = table->hash_to_index(hash);return table->basic_add(index, (u1*)name, (int)strlen(name), hash, false, THREAD);
}

hotspot/src/share/vm/classfile/systemDictionary.cpp
然后是系统字典初始化,系统字典维护者HotSpot对象模型的Klass,initialize_preloaded_classes执行预加载,主要是javabase模块下的类

void SystemDictionary::initialize(TRAPS) {// Allocate arrays_dictionary          = new Dictionary(calculate_systemdictionary_size(PredictedLoadedClassCount));_placeholders        = new PlaceholderTable(_nof_buckets);_number_of_modifications = 0;_loader_constraints  = new LoaderConstraintTable(_loader_constraint_size);_resolution_errors   = new ResolutionErrorTable(_resolution_error_size);_invoke_method_table = new SymbolPropertyTable(_invoke_method_size);// Allocate private object used as system class loader lock_system_loader_lock_obj = oopFactory::new_intArray(0, CHECK);// Initialize basic classesinitialize_preloaded_classes(CHECK);
}

hotspot/src/share/vm/classfile/systemDictionary.cpp
预加载类,包括符号表中定义的类,javabase模块下的类,java基本类型等

void SystemDictionary::initialize_preloaded_classes(TRAPS) {//为Javabase模块创建ModuleEntry ClassLoader::classLoader_init2(CHECK);// 预加载类WKID scan = FIRST_WKID;....//类加载// JSR 292 classesWKID jsr292_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass);WKID jsr292_group_end   = WK_KLASS_ENUM_NAME(VolatileCallSite_klass);initialize_wk_klasses_until(jsr292_group_start, scan, CHECK);initialize_wk_klasses_through(jsr292_group_end, scan, CHECK);//其他类加载//Java基本类型int等....}

hotspot/src/share/vm/classfile/systemDictionary.cpp
取initialize_wk_klasses_until加载路径来看

void SystemDictionary::initialize_wk_klasses_until(WKID limit_id, WKID &start_id, TRAPS) {for (int id = (int)start_id; id < (int)limit_id; id++) {//initialize_wk_klass((WKID)id, opt, CHECK);}
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

bool SystemDictionary::initialize_wk_klass(WKID id, int init_opt, TRAPS) {//查符号表Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid);InstanceKlass** klassp = &_well_known_klasses[id];k = resolve_or_fail(symbol, true, CHECK_0); // load required classreturn ((*klassp) != NULL);
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, bool throw_error, TRAPS)
{return resolve_or_fail(class_name, Handle(), Handle(), throw_error, THREAD);
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) {Klass* klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD);......return klass;
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

Klass* SystemDictionary::resolve_or_null(Symbol* class_name, ...) {//走了这里return resolve_instance_class_or_null(class_name, class_loader, protection_domain, THREAD);
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, ...) { // Do actual loadingk = load_instance_class(name, class_loader, THREAD);
}

hotspot/src/share/vm/classfile/systemDictionary.cpp
九曲十八弯,实际调用加载的地方,由ClassLoader去加载类

nstanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {if (k.is_null()) {// Use VM class loaderk = ClassLoader::load_class(class_name, search_only_bootloader_append, CHECK_(nh));}
}

hotspot/src/share/vm/classfile/classLoader.cpp
创建字节码文件流,每个被加载的Java类都对应着一个ClassLoaderData结构,ClassLoaderData内部通过链表维护着ClassLoader和ClassLoader加载的类

instanceKlassHandle ClassLoader::load_class(Symbol* name, bool search_append_only, TRAPS) {stream = search_module_entries(_exploded_entries, class_name, file_name, CHECK_NULL);ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();instanceKlassHandle result = KlassFactory::create_from_stream(stream, name, ...);
}

hotspot/src/share/vm/classfile/klassFactory.cpp
最终调用ClassFileParser解析Java字节码文件流,字节码文件如何解析后面再谈。

instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,Symbol*name, ...) {//调用类解析ClassFileParser parser(stream,name,loader_data,protection_domain,host_klass,cp_patches,ClassFileParser::BROADCAST, // publicity levelCHECK_NULL);//创建instanceKclass,保存解析结果instanceKlassHandle result = parser.create_instance_klass(old_stream != stream, CHECK_NULL);return result;}

二. 应用类加载

jdk/src/java.base/share/native/libjli/java.c
在HotSpot启动以后,加载我们的Hello类并调用main方法

int JNICALL JavaMain(void * _args){//虚拟机启动if (!InitializeJVM(&vm, &env, &ifn)) {JLI_ReportErrorMessage(JVM_ERROR1);exit(1);}......//加载主类即我们的Hello类mainClass = LoadMainClass(env, mode, what);//获取Hello类的main方法mainID = (*env)->GetStaticMethodID(env, mainClass, "main","([Ljava/lang/String;)V");//调用main方法(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
}

在加载Hello类之前先加载LancherHelper类,由LancherHelper去加载Hello类

static jclass LoadMainClass(JNIEnv *env, int mode, char *name){//LancherHelper类jclass cls = GetLauncherHelperClass(env);//获取LancherHelper类的checkAndLoadMain方法NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,"checkAndLoadMain","(ZILjava/lang/String;)Ljava/lang/Class;"));NULL_CHECK0(str = NewPlatformString(env, name));//使用checkAndLoadMain加载Hello类NULL_CHECK0(result = (*env)->CallStaticObjectMethod(env, cls, mid,USE_STDERR, mode, str));return (jclass)result;
}

先去加载LancherHelper类

jclass GetLauncherHelperClass(JNIEnv *env)
{if (helperClass == NULL) {NULL_CHECK0(helperClass = FindBootStrapClass(env,"sun/launcher/LauncherHelper"));}return helperClass;
}

jdk/src/java.base/unix/native/libjli/java_md_common.c

jclass FindBootStrapClass(JNIEnv *env, const char* classname)
{if (findBootClass == NULL) {//获取jvm.cpp中的JVM_FindClassFromBootLoader方法findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,"JVM_FindClassFromBootLoader");}return findBootClass(env, classname); //调用JVM_FindClassFromBootLoader方法
}

hotspot/src/share/vm/prims/jvm.cpp
调用SystemDictionary解析类去加载类,流程与内部预加载类的加载机制一致

JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,const char* name))//调用SystemDictionary解析类去加载类,流程与内部预加载类的加载机制一致Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END

jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java
加载Hello类

 public static Class<?> checkAndLoadMain(boolean printToStderr,int mode,String what) {//断点显示mode=1,走loadMainClass                                  Class<?> mainClass = (mode == LM_MODULE) ? loadModuleMainClass(what): loadMainClass(mode, what);// record the real main class for UI purposes// neither method above can return null, they will abort()appClass = mainClass;validateMainClass(mainClass);return mainClass;}

使用类加载器加载Hello类,mode=1,what为类名即Hello

private static Class<?> loadMainClass(int mode, String what) {// get the class nameString   cn = what; //简化case// load the main classcn = cn.replace('/', '.');Class<?> mainClass = null;ClassLoader scl = ClassLoader.getSystemClassLoader(); //获取类加载器try {try {mainClass = Class.forName(cn, false, scl); //加载类} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {......}} catch (LinkageError le) {......}return mainClass;}

jdk/src/java.base/share/classes/java/lang/Class.java
Class.forName将进行安全校验并调用Class.c中的forName0

@CallerSensitive
public static Class<?> forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException{Class<?> caller = null;SecurityManager sm = System.getSecurityManager();if (sm != null) {// Reflective call to get caller class is only needed if a security manager// is present.  Avoid the overhead of making this call otherwise.caller = Reflection.getCallerClass();if (VM.isSystemDomainLoader(loader)) {ClassLoader ccl = ClassLoader.getClassLoader(caller);if (!VM.isSystemDomainLoader(ccl)) {sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);}}}return forName0(name, initialize, loader, caller); //forName0是一个native调用}

jdk/src/java.base/share/native/libjava/Class.c

JNIEXPORT jclass JNICALL
Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,jboolean initialize, jobject loader, jclass caller){cls = JVM_FindClassFromCaller(env, clname, initialize, loader, caller);
}

hotspot/src/share/vm/prims/jvm.cpp

JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,jboolean init, jobject loader,jclass caller)){jclass result = find_class_from_class_loader(env, h_name, init, h_loader,h_prot, false, THREAD);
}

hotspot/src/share/vm/prims/jvm.cpp

jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init,Handle loader, Handle protection_domain,jboolean throwError, TRAPS) {//加载Hello类,在前面加载预加载类时也是走resolve_or_failKlass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain,throwError != 0, CHECK_NULL);return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror());
}

三.HotSpot类加载模型

jdk/src/java.base/share/classes/java/lang/ClassLoader.java
获取类加载器,按初始化等级返回相应的类加载器,在VM.java中定义了各等级的含义:

1. JAVA_LANG_SYSTEM_INITED = 1,lang库初始化结束,
2. MODULE_SYSTEM_INITED = 2模块初始化结束,
3. SYSTEM_LOADER_INITIALIZING = 3 初始化中,
4. SYSTEM_BOOTED= 4 系统完全启动,

显然加载Hello类时初始化等级为4 scl为ClassLoader,scl在initSystemClassLoader中被赋值,initSystemClassLoader在HotSpot启动阶段被调用,所以scl不为空。

 @CallerSensitivepublic static ClassLoader getSystemClassLoader() {switch (VM.initLevel()) {case 0:case 1:case 2:return getBuiltinAppClassLoader();case 3:String msg = "getSystemClassLoader should only be called after VM booted";throw new InternalError(msg);case 4:// system fully initializedassert VM.isBooted() && scl != null;SecurityManager sm = System.getSecurityManager();if (sm != null) {checkClassLoaderPermission(scl, Reflection.getCallerClass());}return scl;default:throw new InternalError("should not reach here");}}

jdk/src/java.base/share/classes/java/lang/ClassLoader.java
获取类加载器

  static ClassLoader getBuiltinAppClassLoader() {return ClassLoaders.appClassLoader();}

jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java
ClassLoaders中实现了三种类加载器:BootClassLoader,PlatformClassLoader,AppClassLoader,均继承自BuiltinClassLoader,间接继承自SecureClassLoader,ClassLoader(抽象类);

 static {// -Xbootclasspth/a or -javaagent Boot-Class-PathURLClassPath bcp = null;String s = VM.getSavedProperty("jdk.boot.class.path.append");if (s != null && s.length() > 0)bcp = toURLClassPath(s);// we have a class path if -cp is specified or -m is not specified.// If neither is specified then default to -cp <working directory>// If -cp is not specified and -m is specified, the value of// java.class.path is an empty string, then no class path.URLClassPath ucp = new URLClassPath(new URL[0]);String mainMid = System.getProperty("jdk.module.main");String cp = System.getProperty("java.class.path");if (cp == null)cp = "";if (mainMid == null || cp.length() > 0)addClassPathToUCP(cp, ucp);// create the class loadersBOOT_LOADER = new BootClassLoader(bcp);PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);}

jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java
内建类加载器AppClassLoader如何加载类

 private static class AppClassLoader extends BuiltinClassLoader { //内部类@Overrideprotected Class<?> loadClass(String cn, boolean resolve)throws ClassNotFoundException{// for compatibility reasons, say where restricted package list has// been updated to list API packages in the unnamed module.SecurityManager sm = System.getSecurityManager();if (sm != null) {int i = cn.lastIndexOf('.');if (i != -1) {sm.checkPackageAccess(cn.substring(0, i));}}return super.loadClass(cn, resolve); //调用父类}}

jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java

@Overrideprotected Class<?> loadClass(String cn, boolean resolve)throws ClassNotFoundException{Class<?> c = loadClassOrNull(cn, resolve);if (c == null)throw new ClassNotFoundException(cn);return c;}

jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java

protected Class<?> loadClassOrNull(String cn, boolean resolve) {synchronized (getClassLoadingLock(cn)) {//检查类是否已加载Class<?> c = findLoadedClass(cn);if (c == null) { //没找到// 定位模块LoadedModule loadedModule = findLoadedModule(cn);if (loadedModule != null) {// package is in a moduleBuiltinClassLoader loader = loadedModule.loader();if (loader == this) {if (VM.isModuleSystemInited()) {c = findClassInModuleOrNull(loadedModule, cn);}} else {//代理到其他加载器c = loader.loadClassOrNull(cn);}} else {// 调用父加载器这里指BootClassLoader和PlatformClassLoader,parent由构造器传入if (parent != null) {c = parent.loadClassOrNull(cn);}//父加载没加载,则由当前加载器加载,if (c == null && hasClassPath() && VM.isModuleSystemInited()) {c = findClassOnClassPathOrNull(cn);}}}if (resolve && c != null)resolveClass(c); //解析类return c;}}

jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java

private Class<?> findClassOnClassPathOrNull(String cn) {String path = cn.replace('.', '/').concat(".class");if (System.getSecurityManager() == null) {Resource res = ucp.getResource(path, false);if (res != null) {try {return defineClass(cn, res);} catch (IOException ioe) {// TBD on how I/O errors should be propagated}}return null;} else {// avoid use of lambda herePrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {public Class<?> run() {Resource res = ucp.getResource(path, false);if (res != null) {try {return defineClass(cn, res);} catch (IOException ioe) {// TBD on how I/O errors should be propagated}}return null;}};return AccessController.doPrivileged(pa);}}

jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java

private Class<?> defineClass(String cn, LoadedModule loadedModule) {try {ByteBuffer bb = null;URL csURL = null;......CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null);try {// define class to VMreturn defineClass(cn, bb, cs); //调用顶级父类ClassLoder的defineClass} finally {reader.release(bb);}} catch (IOException ioe) {// TBD on how I/O errors should be propagatedreturn null;}}

jdk/src/java.base/share/classes/java/lang/ClassLoader.java
自定义类加载器加载类时可以不继承自内建类加载器,直接继承自ClassLoader重写loadClass和findClass方法,双亲委派模型可以被破坏

  public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}

jdk/src/java.base/share/classes/java/lang/ClassLoader.java

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 检查是否已加载过Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false); //父类加载} else {c = findBootstrapClassOrNull(name); //从启动类中查找}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name); //查找// this is the defining class loader; record the statsPerfCounter.getParentDelegationTime().addTime(t1 - t0);PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c); //解析}return c;}}

本篇主要从类加载的角度来看HotSpot内部预加载类和用户类的加载以及HotSpot的类加载模型,虚拟机的启动是通过引导类加载器创建一个初始化类来完成的。虚拟机的创建和类的加载为后续类的链接初始化等做好准备的。类的链接(验证,准备,解析)是基于二进制字节码文件的。所以下一篇我们看Java字节码文件

HotSpot 类加载相关推荐

  1. hotspot源码下载

    jdk的开源主要体现openjdk项目上,下面简单介绍一下jdk及其子项目hotspot的源码下载方式. 查看全文 http://www.taodudu.cc/news/show-3524756.ht ...

  2. Java学习教程,Java从入门到精通,全套Java视频教程+笔记+配套工具

    目录 一.大纲 一.Java基础 二.计算机基础 三.工具的使用 四.数据库 五.web前端 六.JavaWeb 七.框架 八.互联网分布式技术 发现身边很多自学java却放弃的,真的挺可惜的. 白白 ...

  3. 从Java程序运行的角度分析JDK1.8下JVM的内存区域划分及变量存储

    (内容归纳于网络,不妥之处可共同商讨) 文章目录 Java程序运行 Java编译器 JVM HotSpot 类加载器 字节码校验器 JVM内存五大区域 JDK1.8 下的方法区 元空间与永久代 JDK ...

  4. 拜托,学妹,别再问我怎么自学 Java 了!和盘托出

    假如有那么残酷的一天,我不小心喝错了一瓶药,一下子抹掉了我这十多年的编程经验,把我变成了一只小白.我想自学 Java,并且想要找到一份工作,我预计需要 6 个月的时间,前提条件是每天都处于高效率的学习 ...

  5. 基准测试神器JMH——详解36个官方例子

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 简介 基准测试是指通过设计科学的测试方法.测试工具和测试系统,实现对一类测试对象的某项性能指标 ...

  6. 以独占方式锁定此配置文件失败.另一个正在运行_JVM深入解析:运行时数据区+HotSpot+JMM+堆+GC+JVM优化+类加载

    Java运行时数据区: Java虚拟机在执行Java程序的过程中会将其管理的内存划分为若干个不同的数据区域,这些区域有各自的用途.创建和销毁的时间,有些区域随虚拟机进程的启动而存在,有些区域则是依赖用 ...

  7. 《深入java虚拟机》读书笔记类加载

    概述 类加载机制是指虚拟机将描述类的数据从Class文件中加载到内存,并进行数据验证.解析.初始化等过程,最后形成可以直接被虚拟机使用的java类型.在java语言中类的加载.链接.初始化等过程并不是 ...

  8. Java的类加载机制

    jvm系列 垃圾回收基础 JVM的编译策略 GC的三大基础算法 GC的三大高级算法 GC策略的评价指标 JVM信息查看 GC通用日志解读 jvm的card table数据结构 Java类初始化顺序 J ...

  9. Java虚拟机笔记(一):类加载机制

    原文地址:https://www.cnblogs.com/study-everyday/p/7009294.html 一.概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解 ...

最新文章

  1. 架构设计:生产者/消费者模式 第6页:环形缓冲区的实现
  2. 真正从零开始了解 Julia
  3. 云场景实践研究第74期:科沃斯
  4. 用Apache Server配置php,cgi服务器
  5. 比较流行的js前端框架
  6. ulink php,【转载】15款USB数字界面横向评测(对比顶级CD转盘)!多看点!
  7. 说说自己写PHP框架的一些感受
  8. 设计师必备超人气设计素材网站
  9. python全栈薪资这么高,你知道大厂面试都问什么吗?
  10. C# 操作IIS方法集合
  11. 移远ec20型号区别_移远EC20CEFDKG PCIE 全网通4G模块 增加B5频段 性价更高
  12. Linux使用Jstack查看Java堆栈快照脚本
  13. 华为 鸿蒙出处,华为商标名“鸿蒙”原来出自《山海经》,网友直呼:“太燃了”...
  14. PostGIS 爆管分析之找出上游阀门
  15. Axure RP 9 for Mac 中文版 专业产品原型设计工具
  16. *6-1 CCF 2015-03-2 数字排序
  17. sze品牌创始人的故事
  18. dhu复试基础——36 水果价格
  19. C++入门第一阶段——基础篇
  20. mysql故障应急_Mysql错误:紧急救助!!!

热门文章

  1. FineReport安装教程和卸载教程
  2. Linux自学之旅-基础命令(挂载命令之mount)
  3. Day48 | 198. 打家劫舍 | 213.打家劫舍Il |337. 打家劫舍 III
  4. html中留言表怎么写,html 留言板:
  5. InitialContext与lookup
  6. MySQL等于号和不等号的陷阱
  7. c# npoi 公式不计算_「小问小答」1225:公式不计算是什么情况,怎么不出结果呢
  8. linux grep 显示行号 前后3行
  9. 文本编辑工具-typora
  10. winbox定时重启adsl的方法