首页 文章 精选 留言 我的

精选列表

搜索[图床管理],共10000篇文章
优秀的个人博客,低调大师

最全的JAVA知识汇总(附讲解和思维导图)

微信公众号:内核小王子 关注可了解更多关于数据库,JVM内核相关的知识; 如果你有任何疑问也可以加我pigpdong[^1] jvm 一行代码是怎么运行的 首先,java代码会被编译成字节码,字节码就是java虚拟机定义的一种编码格式,需要java虚拟机才能够解析,java虚拟机需要将字节码转换成机器码才能在cpu上执行。 我们可以用硬件实现虚拟机,这样虽然可以提高效率但是就没有了一次编译到处运行的特性了,所以一般在各个平台上用软件来实现,目前的虚拟机还提供了一套运行环境来进行垃圾回收,数组越界检查,权限校验等。虚拟机一般将一行字节码解释成机器码然后执行,称为解释执行,也可以将一个方法内的所有字节码解释成机器码之后在执行,前者执行效率低,后者会导致启动时间慢,一般根据二八法则,将百分之20的热点代码进行即时编译。JIT编译的机器码存放在一个叫codecache的地方,这块内存属于堆外内存,如果这块内存不够了,那么JIT编译器将不再进行即时编译,可能导致程序运行变慢。 jvm如何加载一个类 第一步:加载,双亲委派:启动类加载器(jre/lib),系统扩展类加载器(ext/lib),应用类加载器(classpath),前者为c++编写,所以系统加载器的parent为空,后面两个类加载器都是通过启动类加载器加载完成后才能使用。加载的过程就是查找字节流,可以通过网络,也可以自己在代码生成,也可以来源一个jar包。另外,同一个类,被不同的类加载器加载,那么他们将不是同一个类,java中通过类加载器和类的名称来界定唯一,所以我们可以在一个应用成存在多个同名的类的不同实现。 第二步:链接:(验证,准备,解析) 验证主要是校验字节码是否符合约束条件,一般在字节码注入的时候关注的比较多。准备:给静态字段分配内存,但是不会初始化,解析主要是为了将符号引用转换为实际引用,可能会触发方法中引用的类的加载。 第三步:初始化,如果赋值的静态变量是基础类型或者字符串并且是final的话,该字段将被标记为常量池字段,另外静态变量的赋值和静态代码块,将被放在一个叫cinit的方法内被执行,为了保证cinit方法只会被执行一次,这个方法会加锁,我们一般实现单例模式的时候为保证线程安全,会利用类的初始化上的锁。 初始化只有在特定条件下才会被触发,例如new 一个对象,反射被调用,静态方法被调用等。 java对象的内存布局 java中每一个非基本类型的对象,都会有一个对象头,对象头中有64位作为标记字段,存储对象的哈希码,gc信息,锁信息,另外64位存储class对象的引用指针,如果开启指针压缩的话,该指针只需要占用32位字节。 Java对象中的字段,会进行重排序,主要为了保证内存对齐,使其占用的空间正好是8的倍数,不足8的倍数会进行填充,所以想知道一个属性相对对象其始地址的偏移量需要通过unsafe里的fieldOffset方法,内存对齐也为了避免让一个属性存放在两个缓存行中,disruptor中为了保证一个缓存行只能被一个属性占用,也会用空对象进行填充,因为如果和其他对象公用一个缓存行,其他对象的失效会将整个缓存行失效,影响性能开销,jdk8中引入了contended注解来让一个属性独占一个缓存行,内部也是进行填充,用空间换取时间,如何计算一个对象占用多少内存,如果不精确的话就进行遍历然后加上对象头,这种情况没办法考虑重排序和填充,如果精确的话只能通过javaagent的instrument工具。 反射的原理 反射真的慢么? 首先class.forname和class.getmethod 第一个是一个native方法,第二个会遍历自己和父类中的方法,并返回方法的一个拷贝,所以这两个方法性能都不好,建议在应用层进行缓存。 而反射的具体调用有两种方式,一种是调用本地native方法,一种是通过动态字节码生成一个类来调用,默认采用第一种,当被调用15次之后,采用第二种动态字节码方式,因为生成字节码也耗时,如果只调用几次没必要,而第一种方式由于需要在java和c++之间切换,native 方法本身性能消耗严重,所以对于热点代码频繁调用反射的话,性能并不会很差。 属性的反射,采用unsafe类中setvalue来实现,需要传入该属性相对于对象其始地址的偏移量,也就是直接操作内存。其实就是根据这个属性在内存中的起始地址和类型来读取一个字段的值,在LockSupport类中,park和unpark方法,设置谁将线程挂起的时候也有用到这种方式。 动态代理 java本身的动态代理也是通过字节码实现的 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 工具类中需要提供 类加载器,需要实现的接口,拦截器的实现,也就是需要在InvocationHandler中调用原方法并做增强处理。并且这个实现,一定会被放到新生成的动态代理类里。 生成动态代理类的步骤:先通过声明的接口生成一个byte数组,这个数组就是字节流,通过传入的类加载进行加载生成一个class对象,这个class 里面有个构造方法接收一个参数,这个参数就是InvocationHandler,通过这个构造方法的反射获取一个实例类,在这个class里面,接口的实现中会调用InvocationHandler,而这个class对象为了防止生成太多又没有被回收,所以是一个弱引用对象。 jvm的内存模型 并发问题的根源:可见性,原子性,乱序执行 java内存模型定义了一些规则来禁止cpu缓存和编译器优化,happen-before用来描述两个操作的内存的可见性,有以下6条 1.程序的顺序执行,前一个语句对后一个语句可见 (当两个语句没有依赖的情况下还是可以乱序执行) 2.volatile变量的写对另一个线程的读可见 3.happen-before 具有传递性 4.一个线程对锁的释放对另外一个线程的获取锁可见 (也就是一个线程在释放锁之前对共享变量的操作,另外一个线程获取锁后会看的到) 5.线程a调用了线程b的start()方法,那么线程a在调用start方法之前的操作,对线程b内的run()方法可见 6.线程a调用了线程b的join方法,那么线程b里的所有操作,将对线程a调用join之后的操作可见。 jvm的垃圾回收 两种实现:引用计数和可达性分析,引用计数会出现循环引用的问题,目前一般采用可达性分析。 为了保证程序运行线程和垃圾回收线程不会发生并发影响,jvm采用安全点机制来实现stop the world,也就是当垃圾收集线程发起stop the world请求后,工作线程开始进行安全点检测,只有当所有线程都进入安全点之后,垃圾收集线程才开始工作,在垃圾收集线程工作过程中,工作线程每执行一行代码都会进行安全点检测,如果这行代码安全就继续执行,如果这行代码不安全就将该线程挂起,这样可以保证垃圾收集线程运行过程中,工作线程也可以继续执行。 安全点:例如阻塞线程肯定是安全点,运行的jni线程如果不访问java对象也是安全的,如果线程正在编译生成机器码那他也是安全的,Java虚拟机在有垃圾回收线程执行期间,每执行一个字节码都会进行安全检测。 基础垃圾收集算法:清除算法会造成垃圾碎片,清除后整理压缩浪费cpu耗时,复制算法浪费内存。 基础假设:大部分的java对象只存活了一小段时间,只有少部分java对象存活很久。新建的对象放到新生代,当经过多次垃圾回收还存在的,就把它移动到老年代。针对不同的区域采用不同的算法。因为新生代的对象存活周期很短,经常需要垃圾回收,所以需要采用速度最快的算法,也就是复制,所以新生代会分成两块。一块eden区,两块大小相同的survivor区。 新的对象默认在eden区进行分配,由于堆空间是共享的,所以分配内存需要加锁同步,不然会出现两个对象指向同一块内存,为了避免频繁的加锁,一个线程可以申请一块连续内存,后续内存的分配就在这里进行,这个方案称为tlab。tlab里面维护两个指针,一个是当前空余内存起始位置,另外一个tail指向尾巴申请的内存结束位置,分配内存的时候只需要进行指针加法并判断是否大于tail,如果超过则需要重新申请tlab。 如果eden区满了则会进行一次minorGc ,将eden区的存活对象和from区的对象移动到to区,然后交换from和to的指针。 垃圾收集器的分类:针对的区域,老年代还是新生代,串行还是并行,采用的算法分类复制还是标记整理 g1 基于可控的停顿时间,增加吞吐量,取代cms g1将内存分为多个块,每个块都可能是 eden survivor old 三种之一 首先清除全是垃圾的快 这样可以快速释放内存。 如果发现JVM经常进行full gc 怎么排查? 不停的进行full gc表示可能老年代对象占有大小超过阈值,并且经过多次full gc还是没有降到阈值以下,所以猜测可能老年代里有大量的数据存活了很久,可能是出现了内存泄露,也可能是缓存了大量的数据一直没有释放,我们可以用jmap将gc日志dump下来,分析下哪些对象的实例个数很多,以及哪些对象占用空间最多,然后结合代码进行分析。 并发和锁 线程的状态机 线程池参数:核心线程数,最大线程数,线程工厂,线程空闲时间,任务队列,拒绝策略 先创建核心线程,之后放入任务队列,任务队列满了创建线程直到最大线程数,在超过最大线程数就会拒绝,线程空闲后超过核心线程数的会释放,核心线程也可以通过配置来释放,针对那些一天只跑一个任务的情况。newCachedThreadPool线程池会导致创建大量的线程,因为用了同步队列。 synchronized 同步块会有一个monitorenter和多个monitorexist ,重量级锁是通过linux内核pthread里的互斥锁实现的,包含一个waitset和一个阻塞队列。 自旋锁,会不停尝试获取锁,他会导致其他阻塞的线程没办法获取到锁,所以他是不公平锁,而轻量级锁和偏向锁,均是在当前对象的对象头里做标记,用cas方法设置该标记,主要用于多线程在不同时间点获取锁,以及单线程获取锁的情况,从而避免重量级锁的开销,锁的升级和降级也需要在安全点进行。 reentrantlock相对synchronized的优势:可以控制公平还是非公平,带超时,响应中断。 CyclicBarrier 多个线程相互等待,只有所有线程全部完成后才通知一起继续 (调用await 直到所有线程都调用await才一起恢复继续执行) countdownlatch 一个线程等待,其他线程执行完后它才能继续。(调用await后被阻塞,直到其他地方调用countdown()将state减到1 这个地方的其他可以是其他多个线程也可以其他单个任务) semaphore 同一个时刻只运行n个线程,限制同时工作的线程数目。 阻塞队列一般用两个锁,以及对应的条件锁来实现,默认为INTEGER.MAX为容量,而同步队列没有容量,优先级队列内部用红黑树来实现。 如果要频繁读取和插入建议用concurrenthashmap 如果频繁修改建议用 concurrentskiplistmap,copyonwrite适合读多写少,写的时候进行拷贝,并加锁。读不加锁,可能读取到正在修改的旧值。concurrent系列实际上都是弱一致性,而其他的都是fail-fast,抛出ConcurrentModificationException,而弱一致性允许修改的时候还可以遍历。例如concurrent类的size方法可能不是百分百准确。 AQS 的设计,用一个state来表示状态,一个先进先出的队列,来维护正在等待的线程,提供了acquire和release来获取和释放锁,锁,条件,信号量,其他并发工具都是基于aqs实现。 字符串 字符串可以通过intern()方法缓存起来,放到永久代,一般一个字符串申明的时候会检查常量区是否存在,如果存在直接返回其地址,字符串是final的,他的hashcode算法采用31进制相加,字符串的拼接需要创建一个新的字符串,一般使用stringbuilder。String s1 = "abc"; String s2 = "abc"; String s1 = new String("abc"); s1和s2可能是相等的,因为都指向常量池。 集合 vector 线程安全,arraylist 实现 randomaccess 通过数组实现支持随机访问,linkedlist 双向链表可以支持快速的插入和删除。 treeset 依赖于 treemap 采用红黑树实现,可以支持顺序访问,但是插入和删除复杂度为 log(n) hashset 依赖于 hashmap 采用哈希算法实现,可以支持常数级别的访问,但是不能保证有序 linkedhashset 在hashset的节点上加了一个双向链表,支持按照访问和插入顺序进行访问 hashtable早版本实现,线程安全 不支持空键。 hashmap:根据key的hashcode的低位进行位运算,因为高位冲突概率较高,根据数组长度计算某个key对应数组位置,类似求余算法,在put的时候会进行初始化或者扩容,当元素个数超过 数组的长度乘以负载因子的时候进行扩容,当链表长度超过8会进行树化,数组的长度是2的多少次方,主要方便位运算,另一个好处是扩容的时候迁移数据只需要迁移一半。当要放 15个元素的时候,一般数组初始化的长度为 15/0.75= 20 然后对应的2的多少次方,那么数组初始化长度为 32. ConcurrentHashMap 内部维护了一个segment数组,这个segment继承自reentrantlock,他本身是一个hashmap,segment数组的长度也就是并发度,一般为16. hashentry内部的value字段为volatile来保证可见性.size()方法需要获取所有的segment的锁,而jdk8的size()方法用一个数组存储每个segment对应的长度。 io 输入输出流的数据源有 文件流,字节数组流,对象流 ,管道。带缓存的输入流,需要执行flush,reader和writer是字符流,需要根据字节流封装。 bytebuffer里面有position,capcity,limit 可以通过flip重置换,一般先写入之后flip后在从头开始读。 文件拷贝 如果用一个输入流和一个输出流效率太低,可以用transfer方法,这种模式不用到用户空间,直接在内核进行拷贝。 一个线程一个连接针对阻塞模式来说效率很高,但是吞吐量起不来,因为没办法开那么多线程,而且线程切换也有开销,一般用多路复用,基于事件驱动,一个线程去扫描监听的连接中是否有就绪的事件,有的话交给工作线程进行读写。一般用这种方式实现C10K问题。 堆外内存(direct) 一般适合io频繁并且长期占用的内存,一般建议重复使用,只能通过Native Memory Tracking(NMT)来诊断,MappedByteBuffer可以通过FileChannel.map来创建,可以在读文件的时候少一次内核的拷贝,直接将磁盘的地址映射到用户空间,使用户感觉像操作本地内存一样,只有当发生缺页异常的时候才会触发去磁盘加载,一次只会加载要读取的数据页,例如rocketmq里一次映射1g的文件,并通过在每个数据页写1b的数据进行预热,将整个1G的文件都加载到内存。 设计模式 创建对象:工厂 构建 单例 结构型: 门面 装饰 适配器 代理 行为型:责任链 观察者 模版 封装(隐藏内部实现) 继承(代码复用) 多态(方法的重写和重载) 设计原则:单一指责,开关原则,里氏替换,接口分离,依赖反转 微信公众号:内核小王子 关注可了解更多关于数据库,JVM内核相关的知识; 如果你有任何疑问也可以加我pigpdong[^1] 历史文章: JAVA和操作系统交互细节 通过MySQL存储原理来分析排序和锁 最全的微服务知识总结 网络内核之TCP是如何发送和接收消息的

优秀的个人博客,低调大师

2 张图,让你一秒理解 CountDownLatch、CyclicBarrier

CountDownLatch(倒数闩,Latch:门闩) 经常用于 监听某些初始化操作,等 初始化线程 全部执行完毕后,才通知 主线程 继续工作 a) 即 一个线程处于阻塞的状态下,他要收到 多少次通知,才能被 苏醒,并继续往下执行 b) 注意:只能阻塞 一个线程 c) "countDown.countDown() 到了 0,并使得 countDown.await() 苏醒" 之后,仍旧能进行 countDown.countDown(),并且不会报错;但是countDown.getCount() 始终为 0 CyclicBarrier(同步屏障,cyclic:周期的,循环的,barrier:屏障) 场景假设:每个线程代表一个 跑步运动员,当 所有运动员 都准备好,才能一起出发,只要有一个人没有准备好,那么大家都要等待他 a) 注意:阻塞的是 每个线程

优秀的个人博客,低调大师

Windows7部署Android开发环境实战图

本页内容介绍如何在Windows 7系统上安装Android SDK以及如何设置你的开发环境。然后阅读后续文档学习如何安装,配置,并使用该SDK来创建Android应用程序。 准备文件: WIN7 的安装方法与 XP 的安装方式完全相同。 1、安装JDK 运行 jdk-6u14-windows-i586.exe 检查JDK是否安装成功。打开cmd窗口,输入 java –version 查看JDK的版本信息。 2、安装 Eclipse 直接解压缩 Eclipse3.4.1.zip 到指定目录 如:F:\Android\eclipse 运行eclipse.exe,设置Workspace,指定一个开发目录给它就可以了 看到如下界面,Eclipse 安装完毕 3、安装 android-sdk-windows-1.5_r3.zip Android SDK 同 Eclipse 一样,直接解压缩就可以,我把解压缩到文件夹 F:\Android 中 将Android SDK中的tools绝对路径添加到系统PATH中。 打开“系统属性”选择“环境变量” 添加环境变量PATH 值为SDK中tools的绝对路径,如下图 “确定”后,重新启动计算机。 重启计算机以后,进入cmd命令窗口,检查SDK是不是安装成功。 运行 android –h 如果有以下输出,表明安装成功。 4、安装 Android Development Tools (ADT) 打开 Eclipse IDE 进入菜单中的 "Help" -> "Software Updates" 进入选项卡 "Available Software" -> "Add Site" –> "Location:"输入 http://dl-ssl.google.com/android/eclipse 勾选 "Android DDMS" 和 "Android Development Tools" ,"Install" 如下图所示: 一路Next,注意Accept许可,直到完成。 "Yes"重启"Eclipse",完成安装。 5、设定"SDK Location" 打开 Eclipse IDE 进入菜单中的 "Window" -> "Preferences" 打开"Preferences"窗口,选中"Android" 不要管弹出的错误窗口,直接设定"SDK Location"为SDK的安装目录。 "OK"后,再次打开这个窗口,你可以看到SDK列表 6、验证开发环境,并创建HelloWorld测试程序 进入 Eclipse IDE 菜单中的 "File" -> "New" -> "Project" 选择 "Android Project" 下一步 参考下图完成基本信息的填写,(注意最后一栏,如果选Android1.5, "Min SDK Version" 一定是3) 完成创建后,关闭Eclipse的Welcome窗口。 刚刚创建的项目 接下来创建 Android 虚拟设备 AVD 打开cmd控制台,执行 android list target 查看可用的平台 根据 android create avd –name –target格式创建AVD。 这样就完成了自定义的 Android Virtual Device 最后,配置 "Eclipse" 的 "Run Configuration" 进入菜单中的 "Run" -> "Run Configurations" 双击"Android Application"创建一个新的配置文件,设置Name项 指定右侧"Android"选项卡中"Project"项目 在右侧"Target"选项卡中勾选自己创建的"AVD","Apply"后,"Run" 选则启动方式"Android Appliaction" 正常情况下应该是可以看到模拟器界面的 但是我这边出了点错误, [2010-04-01 02:16:07 - HelloWorld] Automatic Target Mode: Preferred AVD 'sdk_1_5' is not available. Launching new emulator. [2010-04-01 02:16:07 - HelloWorld] Launching a new emulator with Virtual Device 'sdk_1_5' [2010-04-01 02:16:07 - Emulator] emulator: ERROR: unknown virtual device name: 'sdk_1_5' [2010-04-01 02:16:07 - Emulator] emulator: could not find virtual device named 'sdk_1_5' 原因是我移动过系统的MyDocument文件夹,去MyDocument所在文件夹的根目录找到 ".android" 文件夹,复制到 "C:\Users\[当前用户名]" 目录中 Ctrl+F11,成功进入模拟器。 如果还有别的问题,尝试 "Project" -> "Clean" 当前项目,重新生成试试 本文转自 wws5201985 51CTO博客,原文链接:http://blog.51cto.com/wws5201985/799670,如需转载请自行联系原作者

优秀的个人博客,低调大师

这张图亮了……

昨天播出的《我是未来》中阿里巴巴集团技术委员会主席王坚带着阿里云城市大脑ET“综艺首秀” 现场“脑洞爆炸”▼对未来的设想?机器智能会带来什么改变?当城市拥有大脑会怎么样? 30年的杭州长什么样?▼ 王坚博士用阿里云ET城市大脑告诉你——“未来,有了大脑的城市,会拥有100万倍的数据,只消耗1/10的水和电,道路越修越窄,把空间还给自然,我们的生活却更加舒服方便,”王坚描述的2050年的杭州,让很多现场观众充满向往。究竟能否实现,让我们一起见证。 (依稀可见的主城区/西湖) 什么是ET城市大脑 ET城市大脑是整个城市的人工智能中枢,可以对整个城市进行全局实时分析,自动调配公共资源,修正城市运行中的Bug,最终将进化成为能够治理城市的超级人工智能。 ET城市大脑的目标是让数据帮助城市来做思考和决策,变的可自我调节、与人类良性互动。

资源下载

更多资源
Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册