Java浮点型数据的精度问题
Java反射
反射的历史由来
反射这一概念最早由编程开发人员Smith在1982年提出,主要指应用程序访问、检测、修改自身状态与行为的能力。这一概念的提出立刻吸引了编程界的极大关注,各种研究工作随之展开,随之而来引发编程革命,出现了多种支持反射机制的面向对象语言。
什么是反射?
Java 反射(Reflection)机制就是在运行状态中,对于 任意 一个类,都能够获取到这个类的所有属性和方法。对于 任意 一个对象,都能够调用它的**任意一个方法和属性(包括私有的方法和属性)。这种动态获取的信息以及动态调用对象的方法的功能**就称为 Java 语言的反射机制。
通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
Java 程序中对象的类型都是在编译期就确定下来的。但通过 Java 反射机制动态创建的对象,这个对象的类型在编译期是未知的。
反射的核心是 JVM 在 运行时 才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。反射机制就是通过 java.lang.Class 类来实现的,在 Java 中,Object 类是所有类的根类,而 Class 类就是描述 Java 类的类。
注:
Class本身就是一个类,Class就是这个类的名称,所以Object也包括Class类。
反射的主要功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用 private 方法);
- 在运行时调用任意一个对象的方法;
- 修改构造函数、方法、属性的可见性。
反射的主要用途
反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 JavaBean、Action 之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类(运行时动态加载),调用不同的方法,这个时候就必须用到反射。对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。
反射的优势
- 提高灵活性和扩展性,降低耦合性,提高自适应能力
- 允许程序创建和控制任何类的对象,无需硬编码目标类
反射的使用
反射机制中会用到一些类,在了解反射是如何使用之前,先介绍一下这些类。
| 类 | 说明 |
|---|---|
| Object | Java 中所有类的超类 |
| Class | 在反射中表示内存中的一个 Java 类,Class 可以代表的实例类型包括,类和接口、基本数据类型、数组 |
| Constructor | 封装了类的构造函数的属性信息,包括访问权限和动态调用信息 |
| Field | 提供类或接口的成员变量属性信息,包括访问权限和动态修改 |
| Method | 提供类或接口的方法属性信息,包括访问权限和动态调用信息 |
| Modifier | 封装了修饰属性,public、protected、static、final、synchronized、abstract 等 |
声明一个接口和一个简单的具体类作为示例代码:
声明一个接口:
1 | public interface IAddress { |
具体的类,请关注各个方法的修饰属性:
1 | public class Person implements IAddress { |
获取Class对象
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class 文件对象),通过字节码文件对象,能够获取想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等)。每一个类对应一个字节码文件,也就对应着一个 Class 类型的对象,也就是字节码文件对象。
反射的各种功能都需要通过 Class 对象来实现,因此,需要知道如何获取 Class对象,主要有以下几种方式:
1 | public class Test { |
判断是否为某个类的实例
一般地,可以使用 instanceof 关键字来判断是否为某个类的实例。同时也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 Native 方法:
1 | public class Test { |
获取父类和接口列表
1 | public class Test { |
创建实例
通过反射来生成对象主要有两种方式:
1 | public class Test { |
获取方法
获取某个 Class 对象的方法集合,主要有以下几个方法:
Method getMethod(String name, Class<?>... parameterTypes):获取“名称是name,参数是parameterTypes”的public的函数(包括从基类继承的、从接口实现的所有public函数)。Method[] getMethods():获取全部的public的函数(包括从基类继承的, 从接口实现的所有public函数)。Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取”名称是name,参数是parameterTypes”,并且是类自身声明的函数,包含public、protected和private方法。Method[] getDeclaredMethods():获取全部的类自身声明的函数,包含public、protected和private方法。Method getEnclosingMethod():如果这个类是“其它类中某个方法的内部类”,调用getEnclosingMethod()就是这个类所在的方法,若不存在,返回null。没懂…
代码示例:
1 | public class Test { |
输出:
1 | 指定的public方法: |
获取构造方法
获取某个 Class 对象的构造方法,主要有以下几个方法:
Constructor<T> getConstructor(Class<?>... parameterTypes):获取参数是parameterTypes的public的构造函数。parameterTypes参数是一个Class对象数组,这些对象按声明的顺序标识构造函数的形参类型。Constructor<?>[] getConstructors():获取全部的public构造函数。Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取参数是parameterTypes的,并且是类自身声明的构造函数,包含public、protected和private。Constructor<?>[] getDeclaredConstructors():获取类自身声明的全部的构造函数,包含public、protected和private。Constructor<?> getEnclosingConstructor():如果这个类是“其它类的构造函数中的内部类”,调用getEnclosingConstructor()就是这个类所在的构造函数,若不存在,返回null。没懂…
代码示例:
1 | public class Test { |
输出:
1 | 指定的public构造函数: |
获取成员变量
获取某个 Class 对象的成员变量,主要有以下几个方法:
Field getField(String name):获取名称是name的public的成员变量(包括从基类继承的, 从接口实现的所有public成员变量)。Field[] getFields():获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量)。Field getDeclaredField(String name):获取名称是name,并且是类自身声明的成员变量,包含public、protected和private成员变量。Field[] getDeclaredFields():获取全部的类自身声明的成员变量,包含public、protected和private成员变量。
获取其他常用信息
Class 类提供了大量的实例方法来获取该 Class 对象所对应的详细信息,Class 类大致包含如下方法,其中每个方法都包含多个重载版本,因此我们只是做简单的介绍,详细请参考 JDK 文档。
获取类内信息
- 构造器:
Constructor<T> getConstructor(Class<?>... parameterTypes) - 包含的方法:
Method getMethod(String name, Class<?>... parameterTypes) - 包含的属性:
Field getField(String name) - 包含的Annotation:
<A extends Annotation> A getAnnotation(Class<A> annotationClass) - 内部类:
Class<?>[] getDeclaredClasses() - 外部类:
Class<?> getDeclaringClass() - 所实现的接口:
Class<?>[] getInterfaces() - 修饰符:
int getModifiers() - 所在包:
Package getPackage() - 类名:
String getName() - 简称:
String getSimpleName()
判断类本身信息的方法
- 是否注解类型:
boolean isAnnotation() - 是否使用了该Annotation修饰:
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) - 是否匿名类:
boolean isAnonymousClass() - 是否数组:
boolean isArray() - 是否枚举:
boolean isEnum() - 是否接口:
boolean isInterface() - obj 是否是该 Class 的实例:
boolean isInstance(Object obj),此方法是Java语言 instanceof 运算符的动态等价物 public boolean isLocalClass()// 类是不是本地类。本地类,就是定义在方法内部的类。public boolean isMemberClass()// 当且仅当基础类是成员类时才返回truepublic native boolean isPrimitive()// 类是不是“基本类型”,包括void和8个基本类型public boolean isSynthetic()// 如果此类是合成类,则返回true;否则返回false。
使用反射获取泛型信息
为了通过反射操作泛型以迎合实际开发的需要,Java 新增了 java.lang.reflect.ParameterizedType、java.lang.reflect.GenericArrayType、java.lang.reflect.TypeVariable、java.lang.reflect.WildcardType 几种类型来代表不能归一到 Class 类型但是又和原始类型同样重要的类型。
ParameterizedType: 一种参数化类型,比如CollectionGenericArrayType: 一种元素类型是参数化类型或者类型变量的数组类型TypeVariable: 各种类型变量的公共接口WildcardType: 一种通配符类型表达式,如?、? extends Number、? super Integer
其他
1 | // 获取类的"annotationClass"类型的注解 (包括从基类继承的、从接口实现的所有public成员变量) |
调用方法
当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。invoke() 方法的原型为:
1 | public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException |
代码示例:
1 | public class Test { |
创建数组
数组在 Java 里是比较特殊的一种类型,它可以赋值给一个 Object Reference。下面我们看一看利用反射创建数组的例子:
1 | public class Test { |
其中的 Array 类为 java.lang.reflect.Array 类。我们通过 Array.newInstance() 创建数组对象,而 newArray 方法是一个 native 方法,它的原型是:
1 | public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { |
反射的一些注意事项
由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
小结
反射作用:
- java反射机制可以获取类名、类的对象、类的属性、方法、继承关系、Annotation注解等信息
优点:
- 提高Java程序的灵活性和扩展性,降低稠合性,提高自适应能力
- 允许程序创建和控制任何类的对象,无需硬编码目标类
缺点:
- 对性能有影响。使用反射是一种解释操作,需要动态访问JVM以满足需求。
- 一般来说,此类访问操作慢于直接执行java代码。若无必要的情况下,不建议基于反射特性进行编码。
小测
1、 Java是一门支持反射的语言,基于反射为Java提供了丰富的动态性支持,下面关于Java反射的描述,哪些是错误的? (多选)
A、Java反射主要涉及的类如Class, Method, Filed,等,他们都在java.lang.reflet包下
B、通过反射可以动态的实现一个接口,形成一个新的类, 并可以用这个类创建对象, 调用对象方法
C、通过反射, 可以突破Java语言提供的对象成员, 类成员的保护机制, 访问一般方式不能访问的成员
D、Java反射机制提供了字节码修改的技术, 可以动态的修剪一个类
解释
1、Class类在java.lang下
2、这是java的动态代理特性
3、反射可以更改类的访问限制;private、protect等关键字是编译期的定义
4、反射可以新增类,但不能修改本身存在的类的字节码
5、通过反射可以调用私有方法
答案:AD
Java高级特性之疑难问题定位-粗糙版
远程调试
server:
- jdk1.4+: -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 (jdk1.5之后,可以不用-Xdebug)
- jdk1.5+:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
- -Xrunjdwp/-agentlib:jdwp: 使用jdwp来运行调试环境
- transport: 通信方式,dt_socket使用的是socket,dt_shmem使用的是共享内存,其中dt_shmem只适用于windows
- server: 是否以作为调试服务端运行jvm
- suspend: 程序启动后先暂停,等待客户端连接
- address: 服务端监听的地址
- onuncaught:当出现uncaught exception 后,是否中断jvm运行
client:
- jdb -connect com.sun.jdi.SocketAttach:port=5005,hostname=localhost
- stop, step, next, cont, locals, print, threads… , 可在jdk中通过help查看
- 也可以通过eclipse,IDEA 远程调试功能连接
IDEA:
- 打开
Run/Debug Configurations窗口 - 点击左上角加号
Add New Configuration - 选择
Remote JVM Debug
内存问题
运行时数据区:
- 堆:新生代、老年代
- 方法区:
- 线程私有区:虚拟机栈、本地方法栈、程序计数器
常见问题:
- “java.lang.OutOfMemoryError:Java heap space”: 堆内存不足。可能为内存泄漏、堆配置过小或配置不合理。可通过-Xms, -Xmx配置
- java.lang.OutOfMemoryError: PermGen : JDK1.7以前,“space“: 永久代(方法区)空间不足。一般为加载类型过多引起。可通过-XX:PermSize和-XX:MaxPermSize配置,也可以查看是否使用-noclassgc 参数,JDK1.8之后为java.lang.OutOfMemoryError: Metaspace” 。
- StackOverFlowError: 栈空间不足。一般为递归调用引起。通过-Xss配置
- java.lang.OutOfMemoryError:可能为直接内存溢出。一般为通过NIO或JNI不断分配内存导致。通过-XX:MaxDirectMemorySize配置
注意:
- 一般不要使用try-catch捕获OOM。
堆内存溢出:
- 当出现java.lang.OutOfMemoryError: Java heap space异常,说明当前的堆内存不足,无法创建更多的Java对象
常见原因:
- 堆内存太小,使用-Xmx 参数增加虚拟机最大堆内存的大小
- Java代码内存泄漏导致的内存不足,属于代码的bug,mat等工具排查
定位方法:
- jmap -histo
打印当前对象的个数和大小 - jmap –histo:live
打印当前存活对象的个数和大小, - 此命令会触发一次full gc
- jstat: -gc
查看gc情况 - 过jmap -heap:format=b
获取内存信息 - 在启动时增加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=”具体的路 径”,当系统OutOfMemory之后,会将内存信息收集下来
堆内存泄漏:
- 已经无用的对象仍然被其它对象引用就造成了内存泄漏
原因:
- 全局变量(特别是容器类) 引用一个对象, 在不需要的使用没有释放。
- 虽然正常情况对象进行了释放, 但是在异常情况下, 由于释放代码没有被执行到导致的缓慢内存泄漏
- runnable类型的对象被new了, 但是没有按照正常的逻辑提交给线程去执行。 runnable这种特殊对象一旦new出来, 会被虚拟机自身所引用, 尽管用户代码中没有显式引用。
jdk图形工具:
- jconsole: 可以查看堆内存和堆外内存分配情况
- jvisualvm: 功能较jconsole多
- mat:下载地址:https://www.eclipse.org/mat/
- Shallow heap: 对象自身占用的内存,Retained heap:除了对象自身内存外,还包括对象引用其他对象引用的内存
- 软引用:如果一个对象只有软引用,而当前堆空间不足,则GC会回收
- 弱引用:不管内存是否足够,都会回收
- 虚引用:是否有虚引用,都不影响对象的回收。好处是,能在被GC的时候收到通知
- jmc: 配合jfr,不仅能够进行监控,还能够分析jvm的profiling和事件。其中jfr在jdk8u262和jdk11中可用,jmc下载地址http://jdk.java.net/jmc/
CPU使用率高
定位高CPU使用率的线程:
- top –H –p PID: 查看java进程各线程CPU的使用情况
查看线程调用栈:
- jstack PID: 查看高CPU使用率的线程在做什么
线程状态:
- Blocking: 现在在等待锁(Lock或synchronized)
- Waiting: 等待其他线程执行某些操作
- Runnable: 就绪或运行状态
- Timed_Waiting: 限时等待
其他原因:
- 线程多,上下文切换次数多. 可通过vmstat查看
- gc次数过多.可通过gc日志查看
死锁
jstack:
- 命令:jstack pid
- 线程占有一个锁:locked
- 线程等待其它线程释放锁:waiting to lock
- 线程占有一个锁,同时执行该锁的wait():
先打印 locked,然后打印— waiting on
GC
GC日志:
- -XX:PrintGCTimeStamps:打印 GC 时间
- -XX:PrintGCDetails :打印 GC 日志;
- -Xloggc: path:保存GC 日志路径。
- jstat –gcutil: 显示垃圾收集信息
常见问题:
- 频繁gc: 堆空间不足
- gc时间长:堆空间过大
- 回收垃圾少:内存泄漏
Linux常用命令
- vmstat:查看CPU上下文切换、中断次数
- pidstat:查看进程CPU、内存、I/O指标
- iostat:查看系统IO情况
- mpstat:查看每个cpu的性能指标
- strace: 查看进程的系统调用
- free:查看系统内存使用情况
- cachestat:查看整个系统缓存命中情况
- cachetop: 查看每个进程命中缓存情况
- sar:查看系统网络收发情况
- tcpdump:抓取分析网络包
Java高级特性之垃圾回收-粗糙版
垃圾回收介绍
jvm内存管理
什么是垃圾回收(Garbage Collection)
- 把不用的内存回收掉
- java采用自动内存管理技术,内存分配后由虚拟机自动管理
优缺点:
- 优点:程序员不需要自己释放内存,只管new对象即可
- 缺点:GC本身有开销,会挤占业务执行资源。
什么是垃圾:
- 不会被访问到的对象是垃圾
引用计数法
原理
- 记录每个对象被引用的数量,当被引用的数量为0时,则标记为垃圾
- 缺点:无法处理循环引用的问题
可达性分析
原理
- 从GC Roots开始遍历对象,没有被遍历到的对象为垃圾
GC Roots:
- 方法栈使用到的参数、局部变量、临时变量等
- 方法区中类静态属性引用的变量
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
垃圾回收算法
清除(sweep)
原理
- 将垃圾对象所占据的内存标记为空闲内存,然后存在一个空闲列表(free list)中。当需要创建对象时,从空闲列表中寻找空闲内存,分配给新创建的对象
优缺点:
- 优点:速度快
- 缺点:容易造成内存碎片,分配效率低
整理(compact)
原理
- 把存活的对象搬到内存的起始位置,然后在连续的空间内顺序分配
优缺点:
- 优点:分配速度快,局部性好
- 缺点:搬运对象麻烦,性能开销大
复制(copy)
原理
- 将内存分为两个部分,并分别用from和to指针来维护。每次只在from指向的内存中分配内存,当发生垃圾回收时,将from指向区域中存活的对象复制到to指向的内存区域,然后将from指针和to指针互换位置。
优缺点:
- 优点:同压缩算法,没有内存碎片。分配速度快,局部性好
- 缺点:可用内存变少,堆空间使用效率低
垃圾收集器
jvm堆划分
jvm将堆划分为新生代和老年代。新生代存放新创建的对象,当对象生存超过一定时间时,会被移动至老年代。新生代采用的 GC 称为minor GC,老年代发生的 GC 称为 full GC 或 major GC,发生full GC会伴随至少一次minor GC
Minor GC
特点:发生次数多,采用时间短,回收掉大量对象
收集器:serial, Parallel Scavenge, Parallel New.均采用复制算法. Serial是单线程,Parallel New可以看成Serial多线程版本. Parallel Scanvenge和Parallel New类似,但更注重吞吐率,且不能与CMS一起使用
Full GC
特点:发生次数少,耗时长
收集器:Serial Old(整理), Parallel Old(整理), CMS(清除). Serial Old是单线程的,Parallel Old可以看成Serial Old的多线程版本. CMS是并发收集器,除了初始标记和重新标记操作需要Stop the world,其它时间可以与应用程序一起并发执行
垃圾回收触发条件
Minor GC
- Eden区空间不足
Full GC
- 老年代空间不足
- 方法区(Metaspace)空间不足
- 通过minor GC进入老年代的平均大小大于老年代的可用内存
- 老年代被写满
- 调用System.GC,系统建议执行full GC,但不一定执行。 《华为Java语言通用编程规范》规则8.7.5:禁止使用主动GC(除非在密码、RMI等方面),尤其是在频繁/周期性的逻辑中
Java安全管理器SecurityManager
SecurityManager应用场景
当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。
Java 安全管理器允许应用程序设置一个安全管理策略,通过安全管理策略实现对应用程序中敏感操作的管理。
安全策略配置文件
默认的安全管理器配置文件
$JAVA_HOME/jre/lib/security/java.policy (Java 8)
$JAVA_HOME/lib/security/default.policy(Java 9+)
当未指定配置文件时,将会使用该配置。
不同版本的 Java 默认的权限配置有所差异,导致部分原来在低版本 Java 上运行的程序,在高版本中可能出现 access denied 的权限异常。
Java 8 内容如下:
1 | // Standard extensions get all permissions by default |
配置文件详解见下文。
启动安全管理器
启动安全管理有两种方式,建议使用启动参数方式。
参数启动方式
-Djava.security.manager
指定配置文件
-Djava.security.manager -Djava.security.policy="E:/java.policy"
编码启动方式
System.setSecurityManager(new SecurityManager());
配置文件简单解释
基本配置原则
在启用安全管理器的时候,配置遵循以下基本原则:
- 没有配置的权限表示没有。
- 只能配置有什么权限,不能配置禁止做什么。
- 同一种权限可多次配置,取并集。
- 统一资源的多种权限可用逗号分割。
默认配置文件解释
第一部分授权:
1 | grant codeBase "file:${{java.ext.dirs}}/*" { |
授权基于路径在 "file:${{java.ext.dirs}}/*" 的class和jar包,所有权限。
第二部分授权:
1 | grant { |
这是细粒度的授权,对某些资源的操作进行授权。具体不再解释,可以查看javadoc。如 RuntimePermission 的可授权操作经查看javadoc如下:
| 权限目标名称 | 权限所允许的操作 | 允许此权限所带来的风险 |
|---|---|---|
| createClassLoader | 创建类加载器 | 授予该权限极其危险。能够实例化自己的类加载器的恶意应用程序可能会在系统中装载自己的恶意类。这些新加载的类可能被类加载器置于任意保护域中,从而自动将该域的权限授予这些类。 |
| getClassLoader | 类加载器的获取(即调用类的类加载器) | 这将授予攻击者得到具体类的加载器的权限。这很危险,由于攻击者能够访问类的类加载器,所以攻击者能够加载其他可用于该类加载器的类。通常攻击者不具备这些类的访问权限。 |
| setContextClassLoader | 线程使用的上下文类加载器的设置 | 在需要查找可能不存在于系统类加载器中的资源时,系统代码和扩展部分会使用上下文类加载器。授予 setContextClassLoader 权限将允许代码改变特定线程(包括系统线程)使用的上下文类加载器。 |
| enableContextClassLoaderOverride | 线程上下文类加载器方法的子类实现 | 在需要查找可能不存在于系统类加载器中的资源时,系统代码和扩展部分会使用上下文类加载器。授予 enableContextClassLoaderOverride 权限将允许线程的子类重写某些方法,这些方法用于得到或设置特定线程的上下文类加载器。 |
| setSecurityManager | 设置安全管理器(可能会替换现有的) | 安全管理器是允许应用程序实现安全策略的类。授予 setSecurityManager 权限将通过安装一个不同的、可能限制更少的安全管理器,来允许代码改变所用的安全管理器,因此可跳过原有安全管理器所强制执行的某些检查。 |
| createSecurityManager | 创建新的安全管理器 | 授予代码对受保护的、敏感方法的访问权,可能会泄露有关其他类或执行堆栈的信息。 |
| getenv.{variable name} | 读取指定环境变量的值 | 此权限允许代码读取特定环境变量的值或确定它是否存在。如果该变量含有机密数据,则这项授权是很危险的。 |
| exitVM.{exit status} | 暂停带有指定退出状态的 Java 虚拟机 | 此权限允许攻击者通过自动强制暂停虚拟机来发起一次拒绝服务攻击。注意:自动为那些从应用程序类路径加载的全部代码授予 “exitVM.“ 权限,从而使这些应用程序能够自行中止。此外,”exitVM” 权限等于 “exitVM.“。 |
| shutdownHooks | 虚拟机关闭钩子 (hook) 的注册与取消 | 此权限允许攻击者注册一个妨碍虚拟机正常关闭的恶意关闭钩子 (hook)。 |
| setFactory | 设置由 ServerSocket 或 Socket 使用的套接字工厂,或 URL 使用的流处理程序工厂 | 此权限允许代码设置套接字、服务器套接字、流处理程序或 RMI 套接字工厂的实际实现。攻击者可能设置错误的实现,从而破坏数据流。 |
| setIO | System.out、System.in 和 System.err 的设置 | 此权限允许改变标准系统流的值。攻击者可以改变 System.in 来监视和窃取用户输入,或将 System.err 设置为 “null” OutputStream,从而隐藏发送到 System.err 的所有错误信息。 |
| modifyThread | 修改线程,例如通过调用线程的 interrupt、stop、suspend、resume、setDaemon、setPriority、setName 和 setUncaughtExceptionHandler 方法 |
此权限允许攻击者修改系统中任意线程的行为。 |
| stopThread | 通过调用线程的 stop 方法停止线程 |
如果系统已授予代码访问该线程的权限,则此权限允许代码停止系统中的任何线程。此权限会造成一定的危险,因为该代码可能通过中止现有的线程来破坏系统。 |
| modifyThreadGroup | 修改线程组,例如通过调用 ThreadGroup 的 destroy、getParent、resume、setDaemon、setMaxPriority、stop 和 suspend 方法 |
此权限允许攻击者创建线程组并设置它们的运行优先级。 |
| getProtectionDomain | 获取类的 ProtectionDomain | 此权限允许代码获得特定代码源的安全策略信息。虽然获得安全策略信息并不足以危及系统安全,但这确实会给攻击者提供了能够更好地定位攻击目标的其他信息,例如本地文件名称等。 |
| getFileSystemAttributes | 获取文件系统属性 | 此权限允许代码获得文件系统信息(如调用者可用的磁盘使用量或磁盘空间)。这存在潜在危险,因为它泄露了关于系统硬件配置的信息以及一些关于调用者写入文件特权的信息。 |
| readFileDescriptor | 读取文件描述符 | 此权限允许代码读取与文件描述符读取相关的特定文件。如果该文件包含机密数据,则此操作非常危险。 |
| writeFileDescriptor | 写入文件描述符 | 此权限允许代码写入与描述符相关的特定文件。此权限很危险,因为它可能允许恶意代码传播病毒,或者至少也会填满整个磁盘。 |
| loadLibrary.{库名} | 动态链接指定的库 | 允许 applet 具有加载本机代码库的权限是危险的,因为 Java 安全架构并未设计成可以防止恶意行为,并且也无法在本机代码的级别上防止恶意行为。 |
| accessClassInPackage.{包名} | 当类加载器调用 SecurityManager 的checkPackageAccess 方法时,通过类加载器的 loadClass 方法访问指定的包 |
此权限允许代码访问它们通常无法访问的那些包中的类。恶意代码可能利用这些类帮助它们实现破坏系统安全的企图。 |
| defineClassInPackage.{包名} | 当类加载器调用 SecurityManager 的 checkPackageDefinition 方法时,通过类加载器的 defineClass 方法定义指定的包中的类。 |
此权限允许代码在特定包中定义类。这样做很危险,因为具有此权限的恶意代码可能在受信任的包中定义恶意类,比如 java.security 或 java.lang。 |
| accessDeclaredMembers | 访问类的已声明成员 | 此权限允许代码查询类的公共、受保护、默认(包)访问和私有的字段和/或方法。尽管代码可以访问私有和受保护字段和方法名称,但它不能访问私有/受保护字段数据并且不能调用任何私有方法。此外,恶意代码可能使用该信息来更好地定位攻击目标。而且,它可以调用类中的任意公共方法和/或访问公共字段。如果代码不能用这些方法和字段将对象强制转换为类/接口,那么它通常无法调用这些方法和/或访问该字段,而这可能很危险。 |
| queuePrintJob | 打印作业请求的开始 | 这可能向打印机输出敏感信息,或者只是浪费纸张。 |
| getStackTrace | 获取另一个线程的堆栈追踪信息。 | 此权限允许获取另一个线程的堆栈追踪信息。此操作可能允许执行恶意代码监视线程并发现应用程序中的弱点。 |
| setDefaultUncaughtExceptionHandler | 在线程由于未捕获的异常而突然终止时,设置将要使用的默认处理程序 | 此权限允许攻击者注册恶意的未捕获异常处理程序,可能会妨碍线程的终止 |
| Preferences | 表示得到 java.util.prefs.Preferences 的访问权所需的权限。java.util.prefs.Preferences 实现了用户或系统的根,这反过来又允许获取或更新 Preferences 持久内部存储中的操作。 | 如果运行此代码的用户具有足够的读/写内部存储的 OS 特权,则此权限就允许用户读/写优先级内部存储。实际的内部存储可能位于传统的文件系统目录中或注册表中,这取决于平台 OS。 |
可配置项详解
当批量配置的时候,有三种模式:
directory/:表示directory目录下的所有.class文件,不包括.jar文件directory/*:表示directory目录下的所有的.class及.jar文件directory/-:表示directory目录下的所有的.class及.jar文件,包括子目录
可以通过${}来引用系统属性,如:
1 | "file:${{java.ext.dirs}}/*" |
顶层抽象类
java.security.Permission 用来定义类所拥有的权限,具体见其子类。
每个子类具体拥有哪些权限,请查看类注释。
问题解决
当出现关于安全管理的报错的时候,基本有两种方式来解决。
取消安全管理器
一般情况下都是无意启动安全管理器,所以这时候只需要把安全管理器进行关闭,去掉启动参数即可。
增加相应权限
若因为没有权限报错,则报错信息中会有请求的权限和请求什么权限,如下:
1 | Exception in thread "main" java.security.AccessControlException: access denied (java.io.FilePermission c:/protect.txt write) |
上面例子,请求资源 c:/protect.txt 的 FilePermission 的写权限没有,因此被拒绝。
也可以开放所有权限:
1 | grant { |
或
1 | grant { |
29Java多线程之线程规范与线程管理
完善中……
28Java多线程之线程池
推荐:
视频教程:https://www.bilibili.com/video/BV1wh411e7nd
对应代码:https://github.com/hellozhaolq/2021-Java-ThreadPool-Tutorial
对于数据库连接,我们经常听到数据库连接池这个概念。因为建立数据库连接是非常耗时的一个操作,其中涉及到网络IO的一些操作。因此就想出把连接通过一个连接池来管理。需要连接的话,就从连接池里取一个。当使用完了,就“关闭”连接,这不是真正意义上的关闭,只是把连接放回池里,供其他人在使用。所以对于线程,也有了线程池这个概念,其中的原理和数据库连接池差不多。