Java
文章平均质量分 89
程序员小潘
Java开发工程师,现居杭州,CSDN博客专家,热衷于分享计算机编程相关知识,欢迎关注~
展开
-
Java线程池execute和submit的区别
ThreadPoolExecutor提供了两种方法来执行异步任务,分别是execute和submit,也是日常开发中经常使用的方法,那么它俩有什么区别呢?原创 2024-06-04 16:44:25 · 822 阅读 · 1 评论 -
ThreadLocalMap为什么用线性探测解决哈希冲突
ThreadLocal 本身不存储值,访问的是当前线程 ThreadLocalMap 里存储的数据副本,实现了线程间的数据隔离。只有当前线程能访问,也就不存在并发访问时的安全问题了。ThreadLocal 的核心是 ThreadLocalMap,它和 HashMap 不同的是:面对哈希冲突时,后者用的是链表法,而前者用的是线性探测法,为什么呢???原创 2024-01-19 11:33:26 · 776 阅读 · 1 评论 -
HashMap为啥要二次Hash
1. 前言HashMap对于Java程序员来说一定不陌生,除了平时开发会经常使用外,它也是面试官非常喜欢问的一个知识点。HashMap是哈希表的一个经典实现,底层数据结构是数组+链表,在JDK8中还引入了红黑树,以解决链表线性查找的效率问题。HashMap设计的非常优秀,源码两千多行,有很多可以拿出来讨论的点,本篇文章主要分析HashMap二次哈希的目的。2. 哈希码的作用首先,我们得先了解哈希码的作用是什么?HashMap底层采用数组+链表/红黑树的数据结构来存储键值对的映射关系,数组就是若干个哈希原创 2022-05-23 15:24:19 · 5622 阅读 · 3 评论 -
JavaAgent查看动态生成类的源码
1. 前言为什么会接触JavaAgent呢?这起源于笔者最近在读Dubbo的源码,Dubbo有一个很有意思的功能——SPI,它可以根据运行时的URI参数,自适应的调用特定的实现类。大致的原理其实也能猜到,无非就是生成一个代理类,反射解析URI参数里的值,然后再调用对应的实现类。虽然大概可以猜到实现原理,但毕竟只是猜想,抱着科学严谨的精神,还是想看看Dubbo的实现源码,此时就有了一个想法,能不能把Dubbo生成的代理对象的Class类Dump下来,然后反编译看看它的源码呢?理论上是完全可行的,原创 2021-10-20 08:33:13 · 448 阅读 · 0 评论 -
如何优化Java异常的效率?
1. 前言在Java语言中,正如Object是所有对象的父类一样,Throwable是所有异常的父类。为什么会有异常类呢?程序是人开发出来的,而人难免是会犯错误的,因此程序可能会运行异常。一旦发生了异常,开发者首先要做的就是定位异常,然后解决异常。如何解决异常那是开发者要做的事情,如何让开发者快速定位到异常,却是Java语言本身的职责。因此,异常的基类Throwable有一个非常重要的属性【stackTrace】,它代表出现异常时,当前线程运行的堆栈信息。通过它,可以快速定位到该异常是在哪个类的哪原创 2021-08-06 20:24:42 · 484 阅读 · 0 评论 -
如何追踪Java对象的访问?
1. 前言在Java中,我们该如何追踪一个对象呢?追踪对象,有意义吗?很多时候,确实没必要去追踪一个对象。对象完成它的使命后,GC会自动帮我们进行垃圾回收,开发者不用担心内存泄漏的问题。但是有时候,对象追踪又很有用,当你需要自己维护一些比较宝贵的资源时,例如:内存、连接等,使用者一旦忘记归还,资源就会发生泄漏,产生严重后果。了解了追踪对象的意义后,接下来要思考的,就是该如何追踪对象了。需求很简单,要能知道对象具体是在哪里被创建的,在哪里被访问过,这里的【哪里】需要精确到具体代码的行数。原创 2021-08-04 21:22:46 · 409 阅读 · 0 评论 -
Java中为什么[this]可以调用当前实例?
1. 前言在刚开始学习Java的时候,大家肯定都接触过this关键字,尤其是在构造函数赋值的时候,如下示例:public class Person { private String name; private int age; public Person(String name, int age) { // 必须加this关键字,否则无法完成成员变量的赋值 this.name = name; this.age = age; }}在构造函数中,如果成员变量名称和参数名称相同时,原创 2021-07-24 11:05:14 · 288 阅读 · 0 评论 -
从指令集角度分析自动拆/装箱
1. 前言Java是一种强类型的语言,这意味着必须为每一个变量声明一种类型。在Java中,一共有8种基本数据类型,且每个基本数据类型都含有对应的包装类型,对应关系如下表:基本类型包装类型byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublebooleanBooleancharCharacter基本类型和包装类型看似功能重合了,有点多余。实则不然,它俩有各自的原创 2021-07-23 21:57:30 · 163 阅读 · 0 评论 -
Java网络IO演变史
前置知识操作系统的几个概念1.用户态和内核态Linux操作系统的体系架构分为用户态和内核态,内核本质上是一个特殊的软件程序,它控制着计算机的硬件资源,例如协调CPU资源、分配内存资源,并为上层应用程序提供稳定的运行环境。用户态即为上层应用程序,它的运行依赖于内核。为了使用户程序也能够访问到内核管理的资源,所以内核必须提供通用的访问接口,这些接口被称为「系统调用」。2.系统调用和软中断系统调用是操作系统提供给应用程序访问的一组通用接口,它使得运行在用户态的进程可以访问内核管理的资源,例如新线程原创 2021-06-07 20:22:44 · 260 阅读 · 3 评论 -
面试官:你说说ThreadLocal为什么会导致内存泄漏?
1. 前言“ThreadLocal为什么会导致内存泄漏,如何避免?”这是笔者在面试阿里时,面试官提出的问题,当时回答的并不好,今天刚好有时间,决定复盘一下,彻底弄清楚内存泄漏的原因,并分享给大家。1.1 何为内存泄漏?首先我们有必要了解,到底何为「内存泄漏」?笔者这里引用百度百科的解释。内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。站在Java的角度来说,就是JVM创建的对象永远原创 2021-02-24 20:36:49 · 2770 阅读 · 6 评论 -
RandomAccess接口有什么用?
最近在看ArrayList源码,发现ArrayList实现了一个很特别的接口:java.util.RandomAccess。这个接口没有任何东西,和java.lang.Cloneable、java.io.Serializable一样,只是一个标记接口,它的作用是什么呢?注释里写的是,这是一个标记接口,表明实现了这个接口的类是支持快速随机访问的。什么意思呢?就是说实现了这个接口的集合,代表它支持通过下标来快速访问元素,例如ArrayList。像LinkedList就没有实现这个接口,因为Linked原创 2020-12-03 20:58:03 · 289 阅读 · 0 评论 -
Java的自动装箱和拆箱
基本类型和包装类型Java是一种强类型的语言,这就意味着必须为每一个变量声明一种类型。在Java中,一共有8种基本数据类型,且每个基本数据类型都含有对应的包装类型,对应关系如下表:基本类型包装类型byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublebooleanBooleancharCharacter1、为什么要有基本类型?首先,基本类型不是一个Obj原创 2020-11-04 21:26:54 · 263 阅读 · 2 评论 -
从Linux底层理解为什么带Buffer的输出流数据写入速度更快
文章目录先说结论BufferedOutputStreamwrite()方法为什么慢?write之后数据就持久化了吗?页高速缓存 Page Cache如何保证数据一定写入物理磁盘?fsync()先说结论使用Java代码操作文件时,不管是读还是写,总是建议使用缓冲区。开发者可以自己创建一个字节数组,每次read/write总是一块数据,而不是一个字节一个字节的读写。为此JDK直接提供了带缓冲区的输入输出流:BufferedInputStream、BufferedOutputStream。先来看一下两者原创 2020-10-10 21:32:37 · 9688 阅读 · 4 评论 -
Cache Line对数据读写性能的影响
文章目录多级缓存-填补内存读写速度与CPU计算速度的鸿沟局部性原理与Cache Line伪共享对齐填充@Contended备注尾巴对于一个程序来说,几乎所有的计算任务都不可能仅通过CPU的计算就可以完成,它至少要和内存打交道:读取运算数据、写入运算结果。现代CPU的算力已经十分强大,相比之下存储设备的IO读写速度却发展的十分缓慢。通常情况下,内存每完成一次读写操作,CPU已经可以进行上百次的运算,为了填补两者速度上的鸿沟,现代CPU不得不加入一层或多层读写速度接近于CPU处理速度的高速缓存Cache。C原创 2020-10-05 13:29:09 · 1703 阅读 · 4 评论 -
Java对象的内存是在哪里分配的?
目录Java内存分配策略栈上分配大对象进入老年代TLAB快慢分配Java内存分配策略当我们使用new关键字去实例化一个对象时,对象的内存在哪里分配?相信很多Java程序员给出的答案都是【堆】,但事实并非绝对如此,JVM为此做了许多优化。对于绝大多数对象,内存的确是在堆中分配的,但是随着JIT编译器的进步、逃逸分析技术的成熟,“Java对象都是在堆中分配内存”这个结论变得不是那么绝对了。针对Java的内存分配策略,笔者这里画了一张简图如下:栈上分配当实例化的对象占用的内存空间较小,且对象没有发原创 2020-09-11 21:12:20 · 10041 阅读 · 0 评论 -
Java是解释执行还是编译执行的?
目录1、解释执行和编译执行的区别1.1、解释执行1.2、编译执行2、Java是解释执行还是编译执行?2.1、解释器和编译器2.2、何时编译?2.2.1、热点探测1、解释执行和编译执行的区别Java代码要想放到JVM里去运行,首先需要经过Javac的编译,将Java代码编译为字节码Class文件。Class文件反汇编后就是一条条JVM指令了,但是这些指令JVM认识,计算机可不认识。JVM想要执行这些指令,该怎么办呢?1.1、解释执行将JVM指令逐行翻译为本地机器码,逐行翻译,逐行执行。优点:程序原创 2020-09-09 19:37:20 · 3028 阅读 · 0 评论 -
DCL单例需要加volatile关键字吗?
目录什么是DCL单例?对象初始化的过程解析Java代码的反汇编指令CPU指令重排序volatile关键字的语义最终结论什么是DCL单例?实现单例模式的方式有很多种,如:饿汉式、懒汉式、枚举等。DCL(Double Check Lock)双重检查加锁,就是懒汉式的一种实现方式,代码实现如下:开启多线程去获取对象,确实T的实例在堆中只会存在一个,单例是可行的,测试代码如下:DCL的方式确实可以实现单例,但它是有缺陷的:线程获取到的对象可能未被初始化。对象初始化的过程Person person原创 2020-08-09 21:39:51 · 3797 阅读 · 2 评论 -
使用大数组对JVM的影响
目录1、难以分配内存2、大对象直接分配在老年代3、容易触发Full GC1、难以分配内存数组可以通过下标快速访问元素,是因为它的内存地址是连续的。绝大多数JVM管理内存的方式,并不是指针碰撞,而是空闲列表。这就意味着,堆中可用内存空间相对比较分散,存在大量内存碎片,JVM要为大数组分配一块连续的内存空间是比较困难的。借用网友的两张图说明。2、大对象直接分配在老年代数组也是对象,且数组越大,对象占用的空间越大。如果大对象直接分配在新生代,首先会导致JVM为其他小对象难以分配内存,过早的原创 2020-08-04 19:56:59 · 3607 阅读 · 0 评论 -
Java逃逸分析之栈上分配内存
目录什么是逃逸分析?栈上分配内存什么是逃逸分析?在很早以前,Java代码从编写完毕到JVM执行至少需要两个过程:javac将Java代码编译成字节码class文件。JVM载入class文件后,由解释器来逐条将字节码指令解释翻译成本地机器码并执行。因此,Java也被称为是一门”解释执行“的语言,由于解释执行比编译执行要慢,所以”Java程序很慢“在早期深入人心。为了解决“解释执行”的...原创 2020-03-19 20:31:18 · 3536 阅读 · 0 评论 -
关于Java是否应该在循环外声明变量的一点思考
目录前言性能和内存性能测试内存测试编译优化总结前言“不要把变量声明在循环体内”,经常看到类似的言论,那么到底有没有必要这么去做呢?首先,将变量声明在循环体外有以下几个缺点:作用域变大,存在被无意引用的风险防止变量命名冲突可读性较差综上,如果“在循环体外声明变量”不能在其他方面(如性能上)带来优化,那么我实在想不出有什么理由需要这么去做。性能和内存在语法的可读性上,“循环外声明...原创 2020-03-15 12:29:40 · 4108 阅读 · 1 评论 -
Java锁的膨胀过程以及一致性哈希对锁膨胀的影响
1、锁优化在JDK6之前,通过synchronized来实现同步效率是很低的,被synchronized包裹的代码块经过javac编译后,会在代码块前后加上monitorenter和monitorexit字节码指令,被synchronized修饰的方法则会被加上ACC_SYNCHRONIZED标识,不论是在字节码中如何表示,作用和功能都是一样的,线程要想执行同步代码块或同步方法,首先需要竞争锁。...原创 2020-02-25 21:53:59 · 13417 阅读 · 6 评论 -
聊聊StringBuffer与StringBuilder
我们知道,String是只读字符串,所引用的字符串一经定义,就无法再修改。对String进行拼接或截取操作会创建新的String对象,如果需要对字符串进行大量修改,使用String性能极低。如下例子,对字符串进行10万次的拼接,String的性能比StringBuilder要慢500倍左右。public static void main(String[] args) { String s ...原创 2020-01-19 19:58:57 · 3340 阅读 · 0 评论 -
GC分代年龄为什么是15?
在JVM中,对象在Eden区诞生,当内存不够用时触发GC进行对象回收,但不是所有的对象都可以被回收,当一个对象还在被引用时就无法回收,此时JVM会将其移动到“幸存者区”。幸存者区内部又分为“From区”和“To区”,在幸存者区,对象仍然要面临GC,每经历一次GC,对象就要在From区和To区之间来回移动,每移动一次对象的GC年龄就加1,当年龄加到15时(不绝对),JVM会将对象移动到老年区。...原创 2019-12-26 19:27:20 · 19204 阅读 · 12 评论 -
你写的单例真的安全吗?
单例模式单例模式的实现方案有两种:饿汉式、懒汉式。网上很多写法都存在一些问题。饿汉式饥饿法则,优先创建实例,需要时直接返回。优点是代码编写简单,缺点是没有达到懒加载的效果,浪费资源。public class Single { private final static Single SINGLE = new Single(); private Single(){} public ...原创 2019-12-18 20:41:31 · 11743 阅读 · 2 评论 -
构建器代替构造器
前言静态工厂方法和构造器有一个共同的局限性:当有大量可选参数时,方法的数量会不受控制。当有大量可选参数时,开发者一般采用方法重载的方式来编写构造器,面对大量的构造器调用者往往不知所云,尤其是参数类型相同的情况下,调用者往往要跟到源码里面去看才知道各个参数的意义。面对这种情况,除了提供静态工厂方法和构造器外,开发者往往还会将类设计成JavaBeans模式。提供一个无参的构造方法,创建一个空对...原创 2019-12-18 20:40:42 · 3132 阅读 · 0 评论 -
用静态工厂方法代替构造器
参考于《Effective Java》前言对于类而言,获得其实例最常见的方式就是提供一个构造器。如果我们不写构造器,编译器会帮我们自动加上一个被public修饰的空构造器。除了提供构造器以外,静态工厂方法也应该被考虑到程序的设计当中。静态工厂方法本质上就是类的一个静态方法,返回值是类的实例对象。通过私有化构造器,无法直接new对象,而是通过运行静态工厂方法获取对象实例。这么做既...原创 2019-12-18 20:40:17 · 3193 阅读 · 0 评论 -
Java泛型擦除
泛型Java泛型(generics)是JDK 5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许程序员在编译时监测非法的类型。使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。泛型对于集合类尤其有用,例如,ArrayList就是一个无处不在的集合类。没有泛型前,集合类存放和取出都是Object,需要手动的进行...原创 2019-11-16 13:47:13 · 3061 阅读 · 0 评论 -
手写Java动态代理
博客内容来自笔者微信公众号。动态代理又包括JDK代理和CGLIB代理。MyBatis框架就用到了动态代理技术,我们只关心Dao接口,而无需关心实现类。动态代理功能十分强大,今天记录一下本人 手动实现动态代理的过程。分析要如何对一个类对象进行代理呢?首先当然是要分析被代理对象,解析它的方法,然后动态的生成一个子类,对父类方法进行重写。拦截方法,将方法的运行交给代理对象去完成,然后在...原创 2019-11-16 13:36:21 · 3208 阅读 · 0 评论 -
IO网络模型之BIO、NIO、AIO
前言网上经常看到各种IO:BIO、NIO、AIO…花了点时间研究了下,大致了解其模型和原理,特整理一份笔记。学习不同的IO模型之前,有几个概念必须先理清楚。同步、异步同步异步关注的是消息的通信机制,也是相对而言的。同步:程序有序性,第二步的执行必须依赖第一步,只有当第一步执行完了,第二步才能开始执行。异步:程序无序,第二步的执行不依赖于第一步,即使第一步没完成,第二步照样执行。JS...原创 2019-11-03 21:30:26 · 4999 阅读 · 0 评论 -
JVM监控工具及一次调优实战记录
jinfojinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息。jinfo [option] pid无参,输出全部的参数和系统属性。-flag name 输出对应名称的...原创 2019-10-19 23:24:40 · 5069 阅读 · 1 评论 -
Java SPI机制
SPI:Service Provider Interface是JDK内置的服务发现机制。是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。SPI使用规范在classpath目录下创建 META-INF/services 目录。创建以接口全限定名命名的文件。在文件中编写实现类的全限定名。多个实现类用换行符分隔。使用ServiceLoader加载服务。例子P...原创 2019-10-18 19:14:35 · 4546 阅读 · 0 评论 -
JVM内存模型
线程私有程序计数器程序计数器是一块很小的内存空间,它是线程私有的,可以认作为当前线程的行号指示器。对于CPU的一个核心来说,任何时刻都只能执行一条指令。核心在不同的线程之间来回切换,为了切换后可以快速定位到下一步应该执行的指令,每一个线程都有自己独立的“程序计数器”,指向下一个应该执行的指令地址。Java栈Java栈描述的是Java方法执行的内存模型。当一个Java方法被...原创 2019-10-18 18:40:15 · 4658 阅读 · 0 评论 -
Java8中的Stream
Java8中的 Stream 是对集合对象功能的增强。Stream的操作类型中间操作一个流后面可以跟多个中间操作,目的是为了对数据金星一些操作,然后生成新的流交给下一个节点。操作是惰性的,只有在进行终止操作时,才会真正开始遍历流。终止操作一个流只能有一个终止操作,终止操作结束后,流就会关闭,再次使用会抛出异常。中间操作再细分:无状态操作不需要知道其他元素的状态,对...原创 2019-10-17 19:07:30 · 4370 阅读 · 0 评论 -
Java8中的Optional
Java8中,引入了Optional类。它的目的是为了解决Java中频繁的判空操作。例子通过一个例子来查看使用Optional的效果。public class Client { public static void main(String[] args) { int l1 = getLength("admin"); int l2 = getLength1("lisa"); S...原创 2019-10-17 19:06:41 · 4749 阅读 · 0 评论 -
Lambda表达式及方法引用
Lambda表达式在JDK8以前,方法能接收的参数都是变量,JDK8之后,支持将函数作为参数传递。/** * @Description: 接口 */@FunctionalInterfacepublic interface Simple { void callback();}JDK8之前/** * @Description: 客户端测试 */public class Cl...原创 2019-10-17 19:05:22 · 4581 阅读 · 0 评论 -
Java程序CPU占用过高排查
昨天博客项目突然宕机,CPU占用接近100%,连敲命令都卡。tomcat日志也把磁盘占满了,十分异常。后来排查发现原来是RabbitMQ的一个消息始终无法被消费,一直存在队列中,导致每秒执行一次消费代码,最终日志把磁盘占满,服务器宕机…排查问题时,用到了Java自带的工具----jstack。觉得非常有用,特记录。##jstack是啥?引用百度百科的解释如下:jstack是jav...原创 2019-10-13 11:19:02 · 5842 阅读 · 1 评论 -
为什么重写equals后建议重写hashCode?
最近在看《Java核心技术卷一》,复习一下Java基础,谈到equals和hashCode,这个知识点我记得大学的时候是学过的,但是我竟一时语塞,回答不上来。离开校门工作一年多,学习各种框架、技术,在通往大牛的道路上乐此不疲,回过头来,反而连最基础的东西都遗忘了,让人不禁唏嘘感慨。好记性不如烂笔头,遂整理笔记记录。首先要了解equals方法是做什么用的?equals方法用于判断两个对象是否相等...原创 2019-10-12 09:20:56 · 4686 阅读 · 0 评论