栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java 知识笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java 知识笔记

跳表:

每个数据都有对应的层数,层数是随机生成的,跳表最左边的层数被认为系统最小层,这个系统最小,可以认为是所有数据随机roll出的层次都包含的层数。

新增一个数据(key,层数),先看最高层,如果最高层的的节点比当前值大那就向下走,如果小向右走,走到不能走了,往下走,如果当前层数已经等于新增数据的层数,把该数据的的那个点创建出来,这样一次就能够创建出属于这个数的结构

优点:从高层开始插入一个数,可以越过大多数

jvm:

JVM的XX参数之布尔类型
公式:-XX:+ 或者 - 某个属性值(+表示开启,-表示关闭)

如何查看一个正在运行中的java程序,它的某个jvm参数是否开启?具体值是多少?

jps -l 查看一个正在运行中的java程序,得到Java程序号。
jinfo -flag PrintGCDetails (Java程序号 )查看它的某个jvm参数(如PrintGCDetails )是否开启。
jinfo -flags (Java程序号 )查看它的所有jvm参数
Case

是否打印GC收集细节

-XX:-PrintGCDetails
-XX:+PrintGCDetails
是否使用串行垃圾回收器

-XX:-UseSerialGC
-XX:+UserSerialGC
61_JVM的XX参数之设值类型
公式:-XX:属性key=属性值value

Case

-XX:metaspaceSize=128m
-XX:MaxTenuringThreshold=15
62_JVM的XX参数之XmsXmx坑题
两个经典参数:

-Xms等价于-XX:InitialHeapSize,初始大小内存,默认物理内存1/64
-Xmx等价于-XX:MaxHeapSize,最大分配内存,默认为物理内存1/4
查看初始默认参数值 -XX:+PrintFlagsInitial

公式:java -XX:+PrintFlagsInitial

    常见配置汇总
      堆设置

      -Xms:初始堆大小-Xmx:最大堆大小-XX:NewSize=n:设置年轻代大小-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5-XX:MaxPermSize=n:设置持久代大小收集器设置

      -XX:+UseSerialGC:设置串行收集器-XX:+UseParallelGC:设置并行收集器-XX:+UseParalledlOldGC:设置并行年老代收集器-XX:+UseConcMarkSweepGC:设置并发收集器垃圾回收统计信息

      -XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-Xloggc:filename并行收集器设置

      -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)并发收集器设置

      -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。


调优总结:

    年轻代大小选择

    响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。属于及时响应、低延迟的应用,这样的应用在jvm堆内存中,对象的存活时间较 短,所以应该将年轻代的内存调大些。年老代大小选择

    响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:

    并发垃圾收集信息持久代并发收集次数传统GC信息花在年轻代和年老代回收上的时间比例 减少年轻代和年老代花费的时间,一般会提高应用的效率吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。较小堆引起的碎片问题
    因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

    -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩

垃圾收集器,cms垃圾收集过程,为什么停顿时间短,有什么缺点,concurrent mode failure怎么办,内存碎片怎么解决,为什么不用标记整理法?

1)初始标记(CMS initial mark) 2)并发标记(CMS concurrent mark) 3)重新标记(CMS remark) 4)并发清除(CMS concurrent sweep)

初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;

并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对 象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;

重 新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的 标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一 些,但也远比并发标记阶段的时间短;

最后是并发清除阶段,清理删除掉标记阶段判断的已经死亡的 对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的

缺点:

1.并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程(或者说处理器的计 算能力)而导致应用程序变慢,降低总吞吐量。

2.CMS收集器无法处理“浮动垃圾”(Floating Garbage),有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。

3.标记清楚算法会导致内存碎片化

出现并发失败要冻结用户线程然后,使用临时启用Serial Old收集器来重新进行老年代的垃圾收集

cms针对内存碎片解决方案是大多时候不理会,当到达一定阈值的时候清理一次。

为什么不用标记清理算法:cms的宗旨就是低停顿时间,如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新 所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用 程序才能进行

CPU占用过高的定位分析思路

先用top命令找出CPU占比最高的ps -ef或者jps进一步定位

定位到具体线程或者代码

ps -mp 进程 -o THREAD,tid,time

-m 显示所有的线程-p pid进程使用cpu的时间-o 该参数后是用户自定义格式将需要的线程ID转换为16进制格式(英文小写格式),命令printf %x 172 将172转换为十六进制jstack 进程ID | grep tid(16进制线程ID小写英文)-A60 Rpc和http调用哪个好?

http具有通用性,但是封装了太多头部信息,效率低,rpc效率高但是并不通用一般用tcp或udp实现;

通常,RPC要求在调用方中放置被调用的方法的接口。**调用方只要调用了这些接口,就相当于调用了被调用方的实际方法,十分易用。**于是,调用方可以像调用内部接口一样调用远程的方法,而不用封装参数名和参数值等操作。

那要想实现这个过程该怎么办呢?
首先,调用方调用的是接口,必须得为接口构造一个假的实现。显然,要使用动态代理。这样,调用方的调用就被动态代理接收到了。

第二,动态代理接收到调用后,应该想办法调用远程的实际实现。这包括下面几步:
• 识别具体要调用的远程方法的IP、端口
• 将调用方法的入参进行序列化
• 通过通信将请求发送到远程的方法中

这样,远程的服务就接收到了调用方的请求。它应该:
• 反序列化各个调用参数
• 定位到实际要调用的方法,然后输入参数,执行方法
• 按照调用的路径返回调用的结果
调用方调用内部的一个方法,但是被RPC框架偷梁换柱为远程的一个方法。之间的通信数据可读性不需要好,只需要RPC框架能读懂即可,因此效率可以更高

数据库同步双写同步方案缺点:导入数据时间长,如果主节点当机会有数据丢失 Tomcat优化:

#优化一:禁用AJP服务,一般是使用Nginx+tomcat的架构,所以用不着AJP协议,所以把AJP连接器禁用。

#优化二:设置线程池,并且调整最大并发线程数

#优化三:设置tomcat运行模式为nio2,tomcat的运行模式有3种:bio、nio、apr,其中nio2是nio的升级版,在 tomcat8中才支持的,建议采用nio2模式。

什么是类加载器?

Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节 流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动 作的代码被称为“类加载器”(Class Loader)。

类加载阶段:

1.加载: 1)通过一个类的全限定名来获取定义此类的二进制字节流。 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入 口。

2.验证:是确保Class文件的字节流中包含的信息符合《Java虚 拟机规范》的全部约束要求

3.准备:准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初 始值的阶段

4.解析:是Java虚拟机将常量池内的符号引用替换为直接引用的过程

5初始化 初始化阶段就是执行类构造器()方法的过程

Redis集群cluster扩容和缩容:

假设现在存在三主三从

扩容步骤:

1.使用docker新建两个节点,进入其中一个6387作为主节点 ,用exec进入容器会新建一个终端exit退出不会导致容器停止,ctrl+p+q退出容器不会导致容器停止

 2.将新增的6387作为master节点加入集群

redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381 6387 就是将要作为master新增节点 6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群 3.重新分派槽号 命令:redis-cli --cluster reshard IP地址:端口号 redis-cli --cluster reshard 192.168.111.147:6381

输入后需要想好怎么分配,加入想平分,可以用16384/maseter  source node可以是all,这样意味着其他主节点将会各自平均拿出一部分给新的6387节点

4.为6387分配从节点。redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID

缩容步骤:

1、redis-cli --cluster check 192.168.111.147:6382,先找到要删除节点的的从节点

redis-cli --cluster del-node 192.168.111.147:6388 5d149074b7e57b802287d1797a874ed7a1a284a8

2、将主节点6387的槽号重新分配,redis-cli --cluster reshard 192.168.111.147:6381

3、选择接受的节点和要删除的节点,分配成功后删除主节点。

集群节点之间的通信:

Redis集群采用P2P的Gossip(流言)协议,Gossip协议工作原理就是节点彼此不断通信交换信息,一段时间后所有的节点都会知道集群完整的信息,这种方式类似流言传播

节点通信基础
Redis-Cluster中的每个 Redis 实例监听两个 TCP 端口,6379(默认)用于服务客户端查询,16379(默认服务端口+10000)用于集群内部通信。集群中节点通信方式如下:每个节点在固定周期内通过特定规则选择几个节点发送ping消息;接收到ping消息的节点用pong消息作为响应;

Gossip协议基本思想就是:

一个节点想要分享一些信息给网络中的其他的一些节点。

于是,它周期性的随机选择一些节点,并把信息传递给这些节点。

这些收到信息的节点接下来会做同样的事情,即把这些信息传递给其他一些随机选择的节点。

容器的优点

敏捷环境:容器技术最大的优点是创建容器实例比创建虚拟机示例快得多,容器轻量级的脚本可以从性能和大小方面减少开销。提高生产力:容器通过移除跨服务依赖和冲突提高了开发者的生产力。每个容器都可以看作是一个不同的微服务,因此可以独立升级,而不用担心同步。版本控制:每一个容器的镜像都有版本控制,这样就可以追踪不同版本的容器,监控版本之间的差异等等。运行环境可移植:容器封装了所有运行应用程序所必需的相关的细节比如应用依赖以及操作系统。这就使得镜像从一个环境移植到另外一个环境更加灵活。比如,同一个镜像可以在 Windows 或 Linux 或者 开发、测试或 stage 环境中运行。标准化: 大多数容器基于开放标准,可以运行在所有主流 Linux 发行版、Microsoft 平台等等。安全:容器之间的进程是相互隔离的,其中的基础设施亦是如此。这样其中一个容器的升级或者变化不会影响其他容器。 函数a调用函数b的过程,是怎么传参的

(18条消息) Java方法调用的过程是怎样的?_Shockang的博客-CSDN博客_jvm调用方法的过程

最具代表性的bianyi优化技术有四种,分别是: 方法内联 逃逸分析 公共子表达式消除 数组边界检查消除

.nio讲讲,实现原理,优缺点:

bio缺点:

    每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write。当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。

NIO 是面向缓冲区,或者面向块编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络。

Java NIO 的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情

对比:

    BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多。BIO 是阻塞的,NIO 则是非阻塞的。BIO 基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。

nio大概流程:

    当客户端连接时,会通过 ServerSocketChannel 得到 SocketChannel。Selector 进行监听 select 方法,返回有事件发生的通道的个数。将 socketChannel 注册到 Selector 上,register(Selector sel, int ops),一个 Selector 上可以注册多个 SocketChannel。注册后返回一个 SelectionKey,会和该 Selector 关联(集合)。进一步得到各个 SelectionKey(有事件发生)。在通过 SelectionKey 反向获取 SocketChannel,方法 channel()。可以通过得到的 channel,完成业务处理。

了解netty么,讲讲netty的设计模型,架构,使用场景

Reactor模型:

    Reactor 主线程 MainReactor 对象通过 select 监听连接事件,收到事件后,通过 Acceptor 处理连接事件当 Acceptor 处理连接事件后,MainReactor 将连接分配给 SubReactorsubreactor 将连接加入到连接队列进行监听,并创建 handler 进行各种事件处理当有新事件发生时,subreactor 就会调用对应的 handler 处理handler 通过 read 读取数据,分发给后面的 worker 线程处理worker 线程池分配独立的 worker 线程进行业务处理,并返回结果handler 收到响应的结果后,再通过 send 将结果返回给 clientReactor 主线程可以对应多个 Reactor 子线程,即 MainRecator 可以关联多个 SubReactor

netty线程模型:

String:intern()

是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用

进程通信

管道(pipe)

有名管道 (namedpipe)

信号量(semaphore)

信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

消息队列(messagequeue)

消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

信号 (sinal)

共享内存(shared memory)

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

套接字(socket)

套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。

回到顶部

线程间的通信方式

锁机制:包括互斥锁、条件变量、读写锁

互斥锁提供了以排他方式防止数据结构被并发修改的方法。 
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。 
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

wait/notify 等待

Volatile 内存共享

队列

BlockingQueue的核心方法

方法类型抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove()poll()take()poll(time,unit)
检查element()peek()不可用不可用

MYSQL:

索引类型:主键,唯一,联合,全文   唯一索引字段可以为空,主键不可以

什么是mvcc?

多版本并发控制。顾名思义,MVCC 是通过数据行的多个版 本管理来实现数据库的 并发控制 。这项技术使得在InnoDB的事务隔离级别下执行 一致性读 操作有了保 证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样 在做查询的时候就不用等待另一个事务释放锁。

快照读:不加锁的简单的 SELECT 都属于快照读

当前读:当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据)

隐藏字段、Undo Log版本链

对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必 要的隐藏列。

trx_id :每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的 事务id 赋值给 trx_id 隐藏列。

roll_pointer :每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo日志 中,然 后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

每次对记录进行改动,都会记录一条undo日志,每条undo日志也都有一个 roll_pointer 属性 ( INSERT 操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些 undo日志 都连起来,串成一个链表:

MVCC实现原理的实现依赖于:隐藏字段、Undo Log、Read View。

个ReadView中主要包含4个比较重要的内容,分别如下:

1. creator_trx_id ,创建这个 Read View 的事务 ID。 说明:只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为 事务分配事务id,否则在一个只读事务中的事务id值都默认为0。

2. trx_ids ,表示在生成ReadView时当前系统中活跃的读写事务的 事务id列表 。

3. up_limit_id ,活跃的事务中最小的事务 ID。

4. low_limit_id ,表示生成ReadView时系统中应该分配给下一个事务的 id 值。low_limit_id 是系 统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。

注意:low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为1, 2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时, trx_ids就包括1和2,up_limit_id的值就是1,low_limit_id的值就是4。

ReadView的规则 有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。

如果被访问版本的trx_id属性值与ReadView中的 creator_trx_id 值相同,意味着当前事务在访问 它自己修改过的记录,所以该版本可以被当前事务访问。

如果被访问版本的trx_id属性值小于ReadView中的 up_limit_id 值,表明生成该版本的事务在当前 事务生成ReadView前已经提交,所以该版本可以被当前事务访问。

如果被访问版本的trx_id属性值大于或等于ReadView中的 low_limit_id 值,表明生成该版本的事 务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。

如果被访问版本的trx_id属性值在ReadView的 up_limit_id 和 low_limit_id 之间,那就需要判 断一下trx_id属性值是不是在 trx_ids 列表中。1. 如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。2. 如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问

了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:

1. 首先获取事务自己的版本号,也就是事务 ID;

2. 获取 ReadView;

3. 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;

4. 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;

5. 最后返回符合规则的数据。

在隔离级别为读已提交(Read Committed)时,一个事务中的每一次 SELECT 查询都会重新获取一次 Read View。

使用 REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之 后的查询就不会重复生成了

Spring: bean的生命周期:

1.bean配置解析   2.bean注册  3.bean合并(父子容器)4.beanclass加载  5.实例化前阶段这里可以用beanpostprosseser扩展   6 通过反射调用bean的构造器创建bean实例  7.属性设值 处理一些注解@Autowire@Reasource  8.aware接口的回调  9.@PostConstruct  10.bean初始化调用InitializingBean接口的afterPropertiesSet方法 2. 调用定义bean的时候指定的初始化方法。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/786076.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号