大数据开发工程师Hive(Hive如何进行优化)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大数据开发工程师Hive(Hive如何进行优化)相关的知识,希望对你有一定的参考价值。
参考技术A1数据存储及压缩优化
针对hive中表的存储格式通常有textfile和orc,压缩格式一般使用snappy。相比于 textfile格式存储,orc占有更少的存储。因为hive底层使用MR计算架构,数据流是hdfs到磁盘再到hdfs,而且会有很多次IO读写操作,所以使用orc数据格式和snappy压缩策略可以降低IO读写,还能降低网络传输量,这样在一定程度上可以节省存储空间,还能提升hql的执行效率;
2 Hive Job优化
① 调节Jvm参数,重用Jvm;
② 合理设置Map个数;
③ 合理设置Reduce个数;
3 Sql语法优化
① 建表优化 :
1) Hive创建表的时候,可以建分区表,分桶表;
2) Hive创建表的时候,可以指定数据存储格式:TextFile、SequenceFile、RCfile 、ORCfile;
② 查询时优化 :
1) 列裁剪,在查询时只读取需要的列,避免全列扫描,不要使用select * from table;
2) 分区裁剪:在查询时只读取需要分区的数据,避免全表扫描;
3) 开启谓词下推:set hive.optimize.ppd = true,默认是true:
a. 将Sql语句中的where谓词逻辑都尽可能提前执行,减少下游处理的数据量;
4) 大表join小表:
a. 开启MapJoin:set hive.auto.convert.join=true:
b. MapJoin是将Join双方比较小的那个表直接分发到各个Map进程的内存中,在 Map进程中进行Join操作, 这样就不用进行Reduce步骤 ,从而提高了速度( 大表left join小表才有效 ,小表left join大表会失效);
5) 大表join大表:
a. SMB Join :Sort Merge Bucket Join(数据不仅分桶了,而且每个桶数据是排好序了);
b. 开启SMB Join之后,底层是根据两个表join字段进行分桶存储,这样的话,两张表就变为了基于桶之间join关联查询,而不是基于整张表的join,减少了笛卡尔积;
6) 少用in,用left semi join替代in:
a. 原始写法:select a.id, a.name from a where a.id in (select b.id from b);
b. 用join改写:select a.id, a.name from a join b on a.id = b.id;
c. left semi join改写:select a.id, a.name from a left semi join b on a.id = b.id;
7) 用union all代替union,因为union all不需要去重,也不需要排序,效率高于union;
(每天1小题,进步1点点)
大数据平台底层技术-JAVA篇-如何动态加载不同版本的 HIVE JDBC 驱动 - 一文读懂JAVA的类加载机制...
1 前言
大家好,我是明哥!
市场上大数据相关岗位和对应的的技能,主要分为三大类:
-
侧重数据平台底层技术:如大数据运维工程师,大数据开发工程师,大数据架构师;
-
侧重数据的加工和分析:如数据仓库工程师/建模工程师,数据工程师/ETL开发,数据分析师;(data engineer, data analyst)
-
侧重数据的深度挖掘:如算法工程师,数据科学家,AI工程师;(data scientist)
其中第一大类,“侧重数据平台底层技术”,需要在底层大数据平台(如CDH/HDP/CDP/TDH等)的基础上,开发一些平台类的工具或应用,来支撑或辅助第二大类和第三大类相关从业人员的日常工作,所以需要扎实的 JAVA 或 SCALA 语言功底,也需要扎实的 LINUX 运维知识。
有鉴于此,后续笔者会推出系列文章,从大数据的角度出发,专门讲述数据平台底层技术 (分为 JAVA 篇与 LINUX 篇)。
本文是“数据平台底层技术-JAVA篇” 之一,讲述如何根据用户的具体情况,动态加载特定目录下的 JAVA 类,这在一些跨平台跨版本的大数据应用中,是一个常见的需求。(比如根据用户的具体情况,加载特定目录下特定版本的 HIVE JDBC 驱动),背后涉及到的知识是 JAVA 的类加载机制。
2 什么是类加载器
-
类加载器,即 Class loaders, 是 JRE 的一部分,负责在 JVM 程序运行时,动态加载 JAVA 类到 JVM 内存中;
-
通过类加载器,JVM 在运行 JAVA 程序时,不需要知道底层的文件系统和文件,即类加载器解耦了 JVM 和操作系统底层的文件系统;
-
我们之所以说类是“动态”加载的,主要是因为运行程序时需要的所有的 JAVA 类,并不是一次性全部加载到 JVM 内存中,而是在需要哪个类时才加载那个类。
3 JRE 中主要有哪些内置的类加载器
3.1. Bootstrap Class Loader
-
bootstrap class loader 是 JVM 核心的一部分,是使用 native 代码编写的,所以在不同的平台上(如windows,linus,macos),有不同的实现;
-
Bootstrap class loader 是所有其它类加载器的父类;( 这里所说的父类,不是普通类继承关系层面上的父类);
-
Bootstrap Class Loader 负载加载 JDK 内部类,这些内部类在目录 $JAVA_HOME/jre/lib 下,比如 rt.jar;
-
Bootstrap Class Loader 打印时会显示为 null (因为其是用 native 代码实现的);
3.2 Extension Class Loader
-
extension class loader 是 bootstrap class loader 的子类;
-
extension class loader 负责加载JDK的扩展类,这些扩展类一般在目录 $JAVA_HOME/lib/ext 下,或系统变量 java.ext.dirs 指定的其它目录;
3.3. System/application Class Loader
-
system class loader, 或称为 application class loader, 是 Extensions classloader 的子类;
-
system class loader 负责加载 JAVA 应用级别的类, 这些类都在环境变量 classpath 指定的目录下,用户在提交 JAVA 程序时,可以通过命令行参数 -classpath 或 -cp 指定环境变量 classpath。
大家可以通过以下示例代码进行验证:
public void printClassLoaders() throws ClassNotFoundException {
System.out.println("Classloader of this class:"
+ PrintClassLoader.class.getClassLoader());
System.out.println("Classloader of Logging:"
+ Logging.class.getClassLoader());
System.out.println("Classloader of ArrayList:"
+ ArrayList.class.getClassLoader());
}
示例输出:
Class loader of this class:sun.misc.Launcher$AppClassLoader@18b4aac2
Class loader of Logging:sun.misc.Launcher$ExtClassLoader@18769467
Class loader of ArrayList:null
4 JRE 中类加载器是怎么工作的
类加载器加载类,遵循了双亲委派模型:
-
“双亲委派模型”的双亲,更严谨来讲,其实是多级单亲;
-
双亲委派模型下,所有的类加载器,除了 bootstrap class loader, 都有一个唯一的父加载器;
-
”双亲委派模型“的父子关系,不是普通 JAVA 类关系层面上的父子继承关系 (extends),而更像是一种有层次之分的组合关系;
-
当类加载器收到一个类或资源的加载请求后,会首先搜索该类或资源是否已经被加载到内存中了,如果已经被加载了的话,不会重复加载该类或资源;如果没有被加载,则会将加载请求委派给父级加载器去加载;(在内存中搜索时,一样会遵循类的可见性,类的可见性的具体含义见下文)
-
父级加载器一样遵循向上委派机制,将加载请求委派给其父级加载器去加载,通过这种向上传导关系,所有的类加载请求,最终都会被传入到启动类加载器(Bootstrap ClassLoader)中;
-
只有当父级加载器反馈无法完成特定类或资源的加载请求时,即父级加载器在它的搜索范围内找不到该类或资源时,子级加载器才尝试在自己的搜索范围内加载类或资源;
-
如果所有的父级加载器在其搜索范围内都找不到该类或资源,且子级加载器在自己的搜索范围内也找不到该类或资源,JVM 就会报 java.lang.ClassNotFoundException;
遵循以上双亲委派模型,当我们需要加载一个应用类到 JVM 中时:
-
system class loader 首先搜索 jvm 内存,发现该类没有被加载过,然后会委派该加载请求给其父类加载器 extension class loader;
-
extension class loader 又会将该加载请求委派给其父类 bootstrap class loader;
-
bootstrap class loader 尝试加载该类,会在其搜索范围内(即目录 $JAVA_HOME/jre/lib)尝试查找该类,找不到;
-
然后 extension class loader 才会尝试加载该类,在其搜索范围内(即目录 $JAVA_HOME/lib/ext 下,或系统变量 java.ext.dirs 指定的其它目录下)尝试查找该类,找不到;
-
然后 system class loader 才会尝试自己加载该类,会在其搜索范围内(环境变量 classpath 指定的目录下)尝试查找该类,查找成功,加载成功。
通过以上双亲委派模型,JVM 确保了:
-
类的唯一性 uniqueness:只有在父类加载器在其搜索范围内找不到类,无法加载类时,子类加载器才会尝试自己加载,这样就避免了重复加载,确保了类的唯一性:比如类 java.lang.Object 在 $JAVA_HOME/jre/lib/rt.jar中,无论哪个类加载器需要加载该类,最终都是委派给处于模型顶端的启动类加载器bootstrap class loader 加载的,而 bootstrap class loader 只会在$JAVA_HOME/jre/lib/rt.jar 中搜索加载该类,因此类 java.lang.Object 在各种加载环境中都是同一个类;
-
类的可见性 Visibility:父类加载器加载的类对子类加载器(加载的类)可见,而子类加载器加载的类对父类加载器(加载的类)不可见:(所谓的可见,即能访问):即 system class loader 加载的类,可见 extension 和 Bootstrap class loaders 加载的类,反过来则不可见;比如,Class A 是 application class loader 加载的,而 class B 是 extensions class loader 加载的, 则此时 Application class loader 加载的其它类,可以访问 A 和 B; 而 extenson class loader 加载的其它:类, 则只可以访问 B 而不能访问A;
-
类的安全性 Security:双亲委派模型实现了类的唯一性和可见性,避免了用户随意定义类加载器加载核心 API 带来的安全隐患,从而进一步确保了类的安全性;
以上双亲委派模型的具体实现,可以参见源码java.lang.ClassLoader
5. JRE 中双亲委派模型的不足与 thread Context Classloaders
有时,JVM 核心类可能需要动态加载应用开发人员提供的特定类或资源:
-
比如 JNDI 中核心功能都是在 rt.jar 的核心内部类中实现的,但在程序运行时,这些 JNDI 类可能需要加载类加载路径下各个 vendor 的具体实现类 (通过命令行参数 -classpath 或 -cp 指定类加载路径的具体目录),此时就需要 bootstrap class loader (parent class loader) 加载本应只对 application class loader 可见的类 (child class loader);
-
再比如 JDBC 中核心功能都是在 rt.jar 的核心内部类中实现的,包括 java.sql.DriverManager/java.sql.Driver等, 但在程序运行时,这些 JDBC 类可能需要加载类加载路径下各个 vendor 的具体实现类 (通过命令行参数 -classpath 或 -cp 指定类加载路径的具体目录),比如 oracle jdbc 驱动 oracle.jdbc.driver.OracleDriver,此时也需要 bootstrap class loader (parent class loader) 加载本应只对 application class loader 可见的类 (child class loader).
此时,以上 J2SE 双亲委派机制就不 work 了。为此,JVM 提供了 thread context class loader:
-
每个线程在创建时,都有一个对应的 ContextClassLoader,来负责该线程中类或资源的加载;
-
当没有显示指定时,线程的 ContextClassLoader 跟其父线程的 context class loader 是同一个;
-
可以通过方法 getContextClassLoader() 获得当前线程的 ContextClassLoader;
-
可以通过方法 setContextClassLoader(ClassLoader cl) 重置当前线程的 ContextClassLoader;
可以通过以下代码,验证 JDBC 相关类的加载器:
Class<?> driverManagerClass = Class.forName("java.sql.DriverManager");
Class<?> driverClass = Class.forName("java.sql.Driver");
Class<?> oracleDriverClass = Class.forName("oracle.jdbc.driver.OracleDriver");
System.out.println("driverManagerClass.getClassLoader():" + driverManagerClass.getClassLoader());
System.out.println("driverClass.getClassLoader():" + driverClass.getClassLoader());
System.out.println("oracleDriverClass.getClassLoader():" + oracleDriverClass.getClassLoader());
对应的输出如下:
driverManagerClass.getClassLoader():null
drivrClass.getClassLoader():null
oracleDriverClass.getClassLoader():sun.misc.Launcher$AppClassLoader@18b4aac2
6 自定义类加载器
6.1 自定义类加载器的应用场景
通过合理使用上述 JVM 自带的各个类加载器,一般就已经能够满足绝大多情况下类或资源的加载需求了。但在某些特殊情况下,我们仍然需要自定义类加载器,这些场景主要有:
-
辅助修改已有的字节码 bytecode, 比如 weaving agents;
-
加载本地磁盘文件系统之外的网络上的类或资源,比如浏览器加载远程web服务器上的 applet;
6.2 如何自定义类加载器
自定义类加载器,只需要继承类 ClassLoader 并复写方法 findClass() 即可。以下示例代码,自定义了一个类加载器,来从指定的文件中加载 byte array 并构建类:
public class CustomClassLoader extends ClassLoader {
@Override
public Class findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassFromFile(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassFromFile(String fileName) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
fileName.replace('.', File.separatorChar) + ".class");
byte[] buffer;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
int nextValue = 0;
try {
while ( (nextValue = inputStream.read()) != -1 ) {
byteStream.write(nextValue);
}
} catch (IOException e) {
e.printStackTrace();
}
buffer = byteStream.toByteArray();
return buffer;
}
}
7 相关测试类
package com.hundsun;
import org.apache.hadoop.conf.Configuration;
import org.apache.hive.jdbc.HiveDriver;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author: liming
* @date: 2021/8/28 20:12
* @description:
*/
public class ClassLoaderTest {
private static String dataSourceDriverPath;
private static String classPath = "E:\\\\git\\\\sparkTest\\\\target";
public static void main(String[] args) {
try {
// dataSourceDriverPath = args[0];
dataSourceDriverPath = "D:\\\\software\\\\数据源驱动-202101.03.000\\\\drivers\\\\hive-cdh6\\\\lib";
// System.out.println("驱动路径:" + dataSourceDriverPath);
System.out.println("ClassLoaderTest.class.getClassLoader():" + ClassLoaderTest.class.getClassLoader().toString());
// System.out.println("ClassLoaderTest.class.getClassLoader().getParent():" + ClassLoaderTest.class.getClassLoader().getParent().toString());
// System.out.println("ClassLoaderTest.class.getClassLoader().getParent().getParent():" + ClassLoaderTest.class.getClassLoader().getParent().getParent());
// System.out.println("ClassLoaderTest.class.getClassLoader().getParent().getParent().getParent():" + ClassLoaderTest.class.getClassLoader().getParent().getParent().getParent());
jdbcDriverTest();
getDriverUsingContextClassLoader();
getDriverUsingUrlClassLoader();
getClassUsingContextClassLoader(classPath);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getCause());
e.printStackTrace();
}
}
public static void jdbcDriverTest(){
try{
Class<?> driverManagerClass = Class.forName("java.sql.DriverManager");
Class<?> driverClass = Class.forName("java.sql.Driver");
Class<?> oracleDriverClass = Class.forName("oracle.jdbc.driver.OracleDriver");
System.out.println("driverManagerClass.getClassLoader():" + driverManagerClass.getClassLoader());
System.out.println("driverClass.getClassLoader():" + driverClass.getClassLoader());
System.out.println("oracleDriverClass.getClassLoader():" + oracleDriverClass.getClassLoader());
// Connection con=DriverManager.getConnection("jdbc:oracle:thin:@//10.20.29.239:1521/orclpdb1","hs_cic","hundsun");
Connection con= DriverManager.getConnection(
"jdbc:oracle:thin:@//10.20.23.215:1521/orcl","hs_dap","hundsun");
System.out.println(con.getAutoCommit());
con.setAutoCommit(false);
System.out.println(con.getAutoCommit());
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select count(1) from HS_DAP.DAP_DATA_TYPE_LIMING");
while (rs.next()) {
System.out.println(rs.getInt(1));
// System.out.println(rs.getString(1) + " " + rs.getInt(2) + " " + rs.getString(3));
}
PreparedStatement ps = con.prepareStatement("delete from HS_DAP.DAP_DATA_TYPE_LIMING where HIVE_DATA_TYPE = ?");
ps.setString(1,"DATE");
ps.execute();
Thread.sleep(1 * 60 * 1000);
con.close();
}catch(Exception e){ System.out.println(e);}
}
public static Driver getDriverUsingUrlClassLoader() throws URISyntaxException {
String driverPath = dataSourceDriverPath;
List<File> files = scanDir(driverPath);
System.out.println(files.get(0).toURI());
Class<?> driver = null;
Class<?> configuration = null;
try {
URL[] urls = new URL[files.size()];
for (int i = 0; i < files.size(); i++) {
urls[i] = files.get(i).toURI().toURL();
// System.out.println(files.get(i).toURI());
}
System.out.println("Thread.currentThread().getContextClassLoader():" + Thread.currentThread().getContextClassLoader());
URLClassLoader urlClassLoader = new URLClassLoader(urls);
System.out.println("urlClassLoader:" + urlClassLoader);
URL resource = urlClassLoader.getResource("core-site-test.xml");
String resourceURI = resource.toURI().toString();
System.out.println("resourceURI:" + resourceURI);
System.out.println("resource.getPath():" + resource.getPath());
driver = urlClassLoader.loadClass("org.apache.hive.jdbc.HiveDriver");
System.out.println("org.apache.hive.jdbc.HiveDriver.class.getClassLoader():" + HiveDriver.class.getClassLoader().toString());
System.out.println("org.apache.hive.jdbc.HiveDriver.class.getClassLoader().getParent():" + HiveDriver.class.getClassLoader().getParent().toString());
configuration = urlClassLoader.loadClass("org.apache.hadoop.conf.Configuration");
System.out.println("org.apache.hadoop.conf.Configuration.class.getClassLoader():" + Configuration.class.getClassLoader().toString());
System.out.println("org.apache.hadoop.conf.Configuration.class.getClassLoader().getParent():" + Configuration.class.getClassLoader().getParent().toString());
System.out.println("Thread.currentThread().getContextClassLoader():" + Thread.currentThread().getContextClassLoader());
System.out.println("urlClassLoader.getParent():" + urlClassLoader.getParent());
Thread.currentThread().setContextClassLoader(urlClassLoader);
System.out.println("Thread.currentThread().getContextClassLoader()" + Thread.currentThread().getContextClassLoader());
return (Driver) driver.newInstance();
} catch (Exception e) {
System.out.println(e.getCause());
System.out.println(e.getMessage());
e.printStackTrace();
}
return null;
}
public static Driver getDriverUsingContextClassLoader() throws URISyntaxException {
String driverPath = dataSourceDriverPath;
List<File> files = scanDir(driverPath);
System.out.println(files.get(0).toURI());
Class<?> driver = null;
Class<?> configuration = null;
try {
URL[] urls = new URL[files.size()];
for (int i = 0; i < files.size(); i++) {
urls[i] = files.get(i).toURI().toURL();
// System.out.println(files.get(i).toURI());
}
ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader();
System.out.println("prevContextClassLoader:" + prevContextClassLoader);
// create a URLClassLoader with the thread ContextClassLoader as parent
URLClassLoader urlContextClassLoader = new URLClassLoader(urls, prevContextClassLoader);
System.out.println("urlContextClassLoader:" + urlContextClassLoader);
URL resource = urlContextClassLoader.getResource("core-site-test.xml");
String resourceURI = resource.toURI().toString();
// System.out.println("g:" +resourceURI);
System.out.println("resource.getPath():" + resource.getPath());
driver = urlContextClassLoader.loadClass("org.apache.hive.jdbc.HiveDriver");
System.out.println("org.apache.hive.jdbc.HiveDriver.class.getClassLoader():" + org.apache.hive.jdbc.HiveDriver.class.getClassLoader().toString());
System.out.println("org.apache.hive.jdbc.HiveDriver.class.getClassLoader().getParent():" + org.apache.hive.jdbc.HiveDriver.class.getClassLoader().getParent().toString());
configuration = urlContextClassLoader.loadClass("org.apache.hadoop.conf.Configuration");
System.out.println("Thread.currentThread().getContextClassLoader():" + Thread.currentThread().getContextClassLoader());
System.out.println("org.apache.hadoop.conf.Configuration.class.getClassLoader():" + org.apache.hadoop.conf.Configuration.class.getClassLoader().toString());
System.out.println("org.apache.hadoop.conf.Configuration.class.getClassLoader().getParent():" + org.apache.hadoop.conf.Configuration.class.getClassLoader().getParent().toString());
System.out.println("urlContextClassLoader.getParent():" + urlContextClassLoader.getParent());
// set the thread contextClassLoader to the newly created urlContextClassLoader
Thread.currentThread().setContextClassLoader(urlContextClassLoader);
System.out.println("Thread.currentThread().getContextClassLoader()" + Thread.currentThread().getContextClassLoader());
return (Driver) driver.newInstance();
} catch (Exception e) {
System.out.println(e.getCause());
System.out.println(e.getMessage());
e.printStackTrace();
}
return null;
}
public static void getClassUsingContextClassLoader(String classPath) throws URISyntaxException {
List<File> files = scanDir(classPath);
System.out.println(files.get(0).toURI());
try {
URL[] urls = new URL[files.size()];
for (int i = 0; i < files.size(); i++) {
urls[i] = files.get(i).toURI().toURL();
// System.out.println(files.get(i).toURI());
}
ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader();
System.out.println("prevContextClassLoader:" + prevContextClassLoader);
System.out.println("prevContextClassLoader.getParent():" + prevContextClassLoader.getParent());
System.out.println("prevContextClassLoader.getParent().getParent():" + prevContextClassLoader.getParent().getParent());
URLClassLoader urlContextClassLoader = new URLClassLoader(urls, prevContextClassLoader);
System.out.println("urlContextClassLoader:" + urlContextClassLoader);
System.out.println("ClassLoader.getSystemClassLoader():" + ClassLoader.getSystemClassLoader());
Thread.currentThread().setContextClassLoader(urlContextClassLoader);
System.out.println("Thread.currentThread().getContextClassLoader()" + Thread.currentThread().getContextClassLoader());
System.out.println("Thread.currentThread().getContextClassLoader().getParent()" + Thread.currentThread().getContextClassLoader().getParent());
System.out.println("ClassLoader.getSystemClassLoader():" + ClassLoader.getSystemClassLoader());
// if you call Class.forName() without specifying the classloader, it will use the system classloader by default;
// Class<?> claz1 = Class.forName("com.hundsun.SimpleApp");
// Class<?> claz1 = Class.forName("com.hundsun.SimpleApp",true,urlContextClassLoader);
// if you call Class.forName() with the same class and same classloader multiple times,
// the class will be loaded and initialized only once
// Class.forName("com.hundsun.SimpleApp",true,urlContextClassLoader);
// Class.forName("com.hundsun.SimpleApp",true,urlContextClassLoader);
// ClassLoader.loadClass will only load the class but will not initialize it - the static code block will not be executed;
Class claz1 = urlContextClassLoader.loadClass("com.hundsun.SimpleApp");
System.out.println("claz1.getClassLoader()" + claz1.getClassLoader());
} catch (Exception e) {
System.out.println(e.getCause());
System.out.println(e.getMessage());
e.printStackTrace();
}
}
/**
* 扫描lib下面的所有jar包
*
* @return
*/
private static List<File> scanDir(String path) {
List<File> list = new ArrayList<>();
File[] files = new File(path).listFiles();
for (File f : files) {
if (f.isFile() && f.getName().endsWith(".jar")) {
list.add(f);
}
if (f.listFiles() != null) {
for (File file : f.listFiles()) {
if (file.isFile() && file.getName().endsWith(".jar")) {
list.add(file);
}
}
}
}
return list;
}
}
8 知识总结
-
JVM 类加载器是JRE的一部分,负责在 JVM 程序运行时,动态加载 JAVA 类到 JVM 内存中;
-
JRE 中内置的类加载器有 sun.misc.Launcher.AppClassLoader/sun.misc.Launcher.ExtClassLoader/java.net.URLClassLoader/bootstrap class loader 等;
-
类加载器加载类,遵循了双亲委派模型,其核心思想是:
-
当类加载器收到一个类或资源的加载请求后,会首先搜索该类或资源是否已经被加载到内存中了,如果已经被加载了的话,不会重复加载该类或资源;(在内存中搜索类或资源时,一样会遵循类的可见性);
-
如果没有被加载,则会将加载请求委派给父级加载器去加载;
-
父级加载器一样遵循向上委派机制,将加载请求委派给其父级加载器去加载,通过这种向上传导关系,所有的类加载请求,最终都会被传入到启动类加载器(Bootstrap ClassLoader)中;
-
只有当父级加载器反馈无法完成特定类或资源的加载请求时,即父级加载器在它的搜索范围内找不到该类或资源时(搜索范围是磁盘上对应的目录),子级加载器才尝试在自己的搜索范围内加载类或资源;
-
-
通过双亲委派模型,JVM 确保了:类的唯一性 uniqueness, 类的可见性 Visibility, 类的安全性 Security;
-
双亲委派模型也有不足,比如 java.sql.DriverManager/java.sq.Driver 等是 jre 核心内部类,在 $JAVA_HOME/jre/lib/rt.jar 中,由 bootstrap classloader 负责加载他们;而 jdbc 实现类如 oracle.jdbc.driver.OracleDriver 是在程序运行时指定的类加载路径下的某个 jar 包中(类加载路径是通过环境变量 classpath 动态指定的),由 application class loader 负责加载他们;所以当 bootstrap classloader 加载的 java.sql.DriverManager, 需要调用 application classloader 加载的 java.sq.Driver 的具体实现类 oracle.jdbc.driver.OracleDriver 时,由于类的可见性 (子类加载器加载的类对父类加载器加载的类默认不可见),按照双亲委派模型,这是不 work 的;
-
双亲委派模型的不足, 可以通过 thread Context Classloaders 来弥补,JDBC/JNDI 等的实现,都使用了thread Context Classloaders;比如方法 java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>) 就借用了 thread context classloader, 来加载 jdbc 实现类如 oracle.jdbc.driver.OracleDriver,具体源码可见:
-
callerCL = Thread.currentThread().getContextClassLoader(); // 获得thread context classloader aClass = Class.forName(driver.getClass().getName(), true, classLoader); // // 使用获得的 thread context classloader,通过 Class.forName 加载 jdbc 实现类如 oracle.jdbc.driver.OracleDriver
-
当 JVM 内置的加载器满足不了需求时,可以自定义类加载器, 自定义类加载器只需要继承类 ClassLoader 并复写方法 findClass() 即可;
-
除了由 JRE 隐式动态加载类,还可以在代码中显示加载类;
-
显示加载类,可以通过 Class.forName(String name, boolean initialize,ClassLoader loader) 或 Class.forName(String name) 加载,其中后者默认会使用 system classloader,且默认会初始化类 (即执行类的静态代码块);
-
显示加载类,也可以通过 classLoader.loadClass(String name) 来加载类,此时只会加载类但不会初始化类(即不会执行类的静态代码块);
-
通过 classLoader.loadClass(String name) 来显示加载类时,类加载器可以是当前类加载器/系统类加载器/线程上下文类加载器/或新创建的URLClassLoader:
-
通过当前类加载器加载:Class.forName("com.hundsun.SimpleApp",true,urlContextClassLoader);
-
通过系统类加载器加载:ClassLoader.getSystemClassLoader().loadClass("com.hundsun.SimpleApp");
-
通过线程上下文类加载器加载:Thread.currentThread().getContextClassLoader().loadClass("com.hundsun.SimpleApp");
-
新建URLClassLoader并进行加载: ClassLoader prevContextClassLoader = Thread.currentThread().getContextClassLoader(); URLClassLoader urlContextClassLoader = new URLClassLoader(urls, prevContextClassLoader);urlContextClassLoader.loadClass("com.hundsun.SimpleApp");
-
新建URLClassLoader时可以指定其父类,当不指定时其父类默认是系统类加载器加载;
-
!关注不迷路~ 各种福利、资源定期分享!欢迎小伙伴们扫码添加明哥微信,后台加群交流学习。
以上是关于大数据开发工程师Hive(Hive如何进行优化)的主要内容,如果未能解决你的问题,请参考以下文章
大数据平台底层技术-JAVA篇-如何动态加载不同版本的 HIVE JDBC 驱动 - 一文读懂JAVA的类加载机制...