概念
全限定名 = 包名 + 类型
对象拷贝:在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 『 = 』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。
CMS GC: 避免在老年代垃圾收集时出现长时间的卡顿,主要通过两种手段来达成: 1、不对老年代进行整理,而是使用空闲列表(free-lists)来管理内存空间的回收 2、在 mark-and-sweep (标记-清除) 阶段的大部分工作和应用线程一起并发执行。
happens-before, 指令重排
垃圾回收
- minor GC: 回收新生代(包括 Eden 和 Survivor 区域),因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快
- major GC: 回收老年代
- full GC: 清理整个jvm堆
class文件 JVM 只认识 .class 文件,它不关心是何种语言生成了 .class 文件,只要 .class 文件符合 JVM 的规范就能运行。 Class 文件是二进制文件,它的内容具有严格的规范,文件中没有任何空格,全都是连续的 0/1。Class 文件 中的所有内容被分为两种类型:无符号数、表。
静态代理、动态代理 Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理。
注解 如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据。比如程序只要读到加了@Test的方法,就知道该方法是待测试方法,又比如@Before注解,程序看到这个注解,就知道该方法要放在@Test方法之前执行。 所谓元注解,就是加在注解上的注解。
动态加载jar
动态加载class
pojo
语法
-
getResourceAsStream
-
transient transient的作用就是把这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。例外是,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
-
finally 1、finally中的代码总会被执行。2、当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。3、finally中有return时,会直接在finally中退出,导致try、catch中的return失效。
数据类型
String:适用于少量的字符串操作的情况 StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况 StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
HashTable HashMap ConcurrentHashMap
stream
- BufferedOutputStream
- 输出的字节值,暂存在内存数组中,放满后,自动批量输出。放不满,flush()手动刷出
- BufferedInputStream
- 读取一批字节值,暂存在内存数组中,可以一个字节一个字节的处理数组中的数据。这一批处理完,再缓存下一批。
- FileInputStream
- FileOutputStream
锁
- 悲观锁、乐观锁
- 自旋锁、适应性自旋锁
- 偏向锁
- 轻量级锁、重量级锁
- 公平锁、非公平锁
- 可重入锁、不可重入锁
- 共享锁、排他锁
synchronized ReentrantLock ReentrantReadWriteLock
JVM
你可以参考Java 虚拟机底层原理知识总结
内存空间
- 堆: 所有线程共享。 整个堆大小 = 年轻代大小 + 年老代大小 + 持久代大小
- 年轻代/新生代
- Eden 伊甸园 : 对象被创建的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。
- Survivor 幸存者 : 用于保存在Eden space内存区域中经过垃圾回收后没有被回收的对象。
- 老年代 :
- 用于存放新生代中经过多次垃圾回收仍然存活的对象,也有可能是新生代分配不了内存的大对象会直接进入老年代。经过多次垃圾回收都没有被回收的对象,这些对象的年代已经足够old了,就会放入到老年代。
- 年轻代/新生代
- 持久代/元空间区:
- 存放Class和Meta的信息,Class在被Load的时候被放入这个区域。 在JDK8开始有了元空间区(Matespace)来替换永久代(Permanent Generation)。8之后的元空间区属于本地内存,不属于jvm空间。
- 代码缓存区:
- 程序计数器:
- 一块较小的内存空间,是当前线程正在执行的那条字节码指令的地址.若当前线程正在执行的是一个本地方法,那么此时程序计数器为Undefined. 线程私有,每条线程都有自己的程序计数器, 随着线程的创建而创建,随着线程的结束而销毁。这是唯一一个不会出现OutOfMemoryError的内存区域。
- 栈
- C栈 本地方法栈
- JVM栈: 线程私有,随着线程创建而创建,随着线程的结束而销毁
- 局部变量表
- 操作数栈
- 动态链接
- 方法出口信息
老年代问题:
- 长时间存活的对象:老年代主要存放对象的生命周期较长的对象,这些对象在堆内存中存活时间较长。如果程序中存在大量长时间存活的对象,而且这些对象无法被垃圾收集器回收,就会导致老年代被填满。
- 内存泄漏:内存泄漏是指程序中存在无法访问的对象,但占用着内存空间。如果存在内存泄漏,这些对象会逐渐堆积在老年代中,最终导致老年代被填满。
- 大对象:如果程序中存在大对象,它们会直接分配到老年代中。如果这些大对象的数量过多或者占用的内存空间很大,就可能导致老年代被填满。 当老年代被填满时,垃圾收集器会进行Full GC(全局垃圾收集),尝试回收老年代中的垃圾对象。如果Full GC的频率过高或者回收效率低下,可能会导致应用程序的性能下降和内存压力增大。因此,需要对程序的内存使用情况进行监控和调优,以避免老年代被填满的情况发生。
类的生命周期
类的运行时绑定。类加载过程包括加载、验证、准备、解析、初始化。
JVM空间配置参数
- -Xms 初始堆大小
- -Xmx 限制最大堆大小
- -Xmn 年轻代大小
- -XX:NewSize 新生代最小空间大小。
- -XX:MaxNewSize 新生代最大空间大小。
- -XX:PermSize 持久代大小 jdk7
- -XX:MaxPermSize 限制最大持久代 jdk7
- -XX:MetaspaceSize 元空间大小 jdk8
- -XX:MaxMetaspaceSize 限制最大元空间 jdk8
- -Xss 每个线程的栈大小 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试
- -XX:NewRatio 年轻代空间和老年代空间的比值
- -XX:SurvivorRatio 年轻代空间中Eden空间和Survivor空间的比值
- -XX:ParallelGCThreads=
在未明确指定的情况下,JVM会根据逻辑核数ncpus,采用以下公式来计算默认值:当ncpus小于8时,ParallelGCThreads = ncpus; 否则 ParallelGCThreads = 8 + (ncpus - 8 ) ( 5/8 )
jmap命令
- jmap -heap pid 展示pid的整体堆信息
- jmap -histo pid 展示class的内存情况
- jmap -dump:live,format=b,file=a.log pid 将内存使用的详细情况输出到文件
项目构建
maven
- Maven 相关内置变量有哪些:
gradle
ant
ivy
有用的maven插件
- maven-compiler-plugin 指定JDK的版本
- maven-shade-plugin 将依赖的jar包也打包到当前jar包,对依赖的jar包进行重命名以隔离
- maven-assembly-plugin 打jar包, 自定义格式包assembly.xml
- maven-surefire-plugin 执行测试用例
- os-maven-plugin 自动创建系统常用的属性变量
- protobuf-maven-plugin 自动将proto生产代码
- scala-maven-plugin scala和java两种代码混合编译
- build-helper-maven-plugin 支持自定义的项目目录结构(相对于Maven默认目录结构来说)。
- https://github.com/git-commit-id/git-commit-id-maven-plugin
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<forkMode>once</forkMode>
<argLine>-Xmx102400m -Xms51200m -XX:MaxDirectMemorySize=1024m</argLine>
</configuration>
- slf4j-api、slf4j-log4j12、log4j之间关系
- lombok
强大的三方库
guava
Google开源的基础类库, 包含了对:集合,缓存,并发库,字符串处理, I/O等各个方面的支持
本地缓存,guava提供的cache是一个简洁、高效,易于维护的。
字符串处理
新集合类型
MyBatis
数据库(持久层)框架
Spring
Spring框架为开发Java应用程序提供了全面的基础架构支持。 Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平了道路。
Spring Data JPA
Netty
netty比nio好在哪儿?
jni
c/c++
Native.synchronizedLibrary
JNA
https://github.com/java-native-access/jna 开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。
Java SPI META-INF/services
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。
py4j
https://www.py4j.org/ https://www.cnblogs.com/xun-meng/p/12195854.html Py4J 使得 Python 程序可以利用 Python 解释器直接调用Java虚拟机中的 Java 对象,也可以让 Java 调用 Python 对象,有点像 Python 版的 JNI。 PySpark就是使用Py4J来进行Java和Python的互通。 Java和Python分别运行在独立的进程中,通过RPC调用。
下面是python代码访问java对象的例子。 在例子中Python借助GatewayServer来访问JVM。先启动java程序,后运行python程序。
import py4j.GatewayServer;
import java.util.Random;
public class EntryPoint {
private static final Random random = new Random();
public int randInt() { //这个是目标方法,我们让python调用它
return random.nextInt();
}
public static void main(String[] args) {
EntryPoint app = new EntryPoint();
GatewayServer gatewayServer = new GatewayServer(app); // 实例化entry_point, 允许 Python 程序通过本地网络套接字与 JVM 通信
gatewayServer.start();
System.out.println("Gateway Server Started");
}
}
from py4j.java_gateway import JavaGateway
gateway = JavaGateway()
if __name__ == '__main__':
a = gateway.entry_point.randInt() # 从 Java-EntryPoint 入口点调用randInt函数
print(a)
下面这个例子是java回调python实现的方法。 在例子中用python代码去implement出java的interface。先启动java程序,后运行python程序。本质上是python调用java,然后java由回调了python方法。
public interface FunInterface { // 这是java接口,我们希望用python实现它
Object fun(Object source);
}
import py4j.GatewayServer;
public class EntryPoint {
public String input = "xxx";
public void callFun(FunInterface obj) { // 这个是目标方法,我们让python调用它,但其实现却又是python实现
obj.fun(input)
}
public static void main(String[] args) {
EntryPoint app = new EntryPoint();
GatewayServer gatewayServer = new GatewayServer(app); // 实例化entry_point,允许Python程序通过本地网络套接字与 JVM 通信
gatewayServer.start();
System.out.println("Gateway Server Started");
}
}
class PythonListener(object):
def __init__(self, gateway): # 构造函数传入JavaGateway
self.gateway = gateway
class Java:
implements = ["Java接口全限定名.xx.xx.FunInterface"] # 实现一个java中定义的interface
def fun(self, input):
output = "this method is implemented by python"
return output
if __name__ == "__main__":
gateway = JavaGateway(callback_server_parameters=CallbackServerParameters())
listener = PythonListener(gateway)
gateway.entry_point.callFun(listener) # 从 Java-EntryPoint 入口点调用callFun函数
上面两个例子都是先启动java程序作为GateWayServer,然后让python主动发起调用的。 第一个例子:让python访问了java实现的方法; 第二个例子:让java访问python实现的方法。(通过回调接口) 如果你想让java程序主动”发起调用”,可以直接让java程序开启一个shell进程去打开python脚本。