1 double转tostring

  1. double d = 1234567892.50;
  2. NumberFormat format = NumberFormat.getInstance();
  3. format.setMinimumFractionDigits(7);
  4. String s= format.format(d);
  5. System.out.println(s);

Double整数部分超6位用科学计数法表示。

double 与BigDecimal 需要额外精度,查过8会使用科学计算法。

1,234,567,892.501111
https://www.cnblogs.com/yuanlinjie/p/12900798.html

2,java一个对象占用多少字节?数组呢?

https://blog.csdn.net/zzx410527/article/details/93646925

3 java lamda原理

为了更好的支持动态类型语言,Java7通过JSR292给JVM增加了一条新的字节码指令:invokedynamic。之后,JVM上面的一些动态类型语言,比如Groovy(2.0+)和JRuby(1.7.0+)都开始支持invokedynamic。不过让人意外的是,为动态语言量身定制的invokedynamic指令,居然也被用到了Java8的Lambda表达式(JSR335)实现上。本文会对invokedynamic(以下简写做indy)指令做出详细解释。
https://segmentfault.com/a/1190000019577184

4 lambda表达式用法

1.替匿名内部类

  1. 此外,因为Lambda的引入,集合操作也得到了极大的改善。比如,引入stream API,把map、reduce、filter这样的基本函数式编程的概念与Java集合结合起来
  2. Stream都有两种模式:顺序执行和并行执行。

关于Folk/Join框架
应用硬件的并行性在java 7就有了,那就是 java.util.concurrent 包的新增功能之一是一个 fork-join 风格的并行分解框架,同样也很强大高效,有兴趣的同学去研究,这里不详谈了,相比Stream.parallel()这种方式,我更倾向于后者

5 Stream多线程并行数据处理

https://blog.csdn.net/sunjin9418/article/details/53143588/
https://blog.csdn.net/gududedabai/article/details/81513640

6 Java内存泄漏(必考)

https://blog.csdn.net/weter_drop/article/details/89387564 整理一部分
https://blog.csdn.net/duoduo18up/article/details/81545958 为整理
1,HashMap
2.ThreadLocal
3,weakHashMap
4.finalize

  1. 静态集合类,如HashMap、LinkedList等等
  2. 各种连接,如数据库连接、网络连接和IO连接等
  3. 变量不合理的作用域。一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生
  4. 内部类持有外部类,如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露
  5. Handler内存泄露
  6. ExecutorCompletionService用于有返回值,必须take,不然将造成内存泄漏

    7 浏览器使用url整个过程

    8 stream中的坑

    1.collections.tomap key重复了会怎样?怎样解决?
    最后可以加入,提够了一种,选择哪个的策略9 面试 - 图1
    2.当value为null的时候会抛出空指针异常
    is based on Map.merge:
    If the value is a String, then this might work: map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> Optional.ofNullable(e.getValue()).orElse(“”)))

  7. 一个 Stream 只可以使用一次需要再进行相关操作,新建stream

  8. Java 8 stream 尝试了parallel()。数据源是HashSet的,在做分割的时候发现每次跟串行的结果都不一样。 我们此处可以判断,在并行操作中,出现了线程安全问题,并行操作中,只有parallelStorage.add(e);的时候存在 解决方式:5 parallelStream.forEach是不保证顺序的,如果要保证顺序正确,则使用 forEachOrdered。 6 optional.orElse()尽量传入变量,不要传方法,要使用方法庆使用orElseGet,orElse()不管命中与否都会执行,而orElseGet不会
    • 后来改为有序的LinkedHashSet再使用parallel。
    • 去掉parallel,让stream串行执行

      9 日志影响性能

      10 两个对象 相互引用会被gc吗

      1.普通对象会被gc
      2.内部类对象和外部类对象不会

      11 Jmeter 压力测试为什么会丢包

      12 给定 list,表示 id,可能重复。要求用 stream 实现 list -> map 的转换,map 统计 id 出现的次数

      13 内部类为何容易内存泄漏

内部类的实现其实是通过编译器的语法糖实现的,通过生成相应的子类即以OutClassName$InteriorClassName命名的Class文件。并添加构造函数,在构造函数中传入外部类,这也是为什么内部类能使用外部类的方法与字段的原因。
1 当外部类与内部类生命周期不一致的时候很有可能发生内存泄漏,例如在一个Activity启动一个Thread执行一个任务,因为Thread是内部类持有了Activity的引用,当Activity销毁的时候如果Thread的任务没有执行完成,造成Activity的引用不能释放,Activity不能被释放而引起了内存泄漏。
2 声明static的内部类来解决问题,从反编译中可以看出,声明为static的类不会持有外部类的引用,如果你想使用外部类的话,可以通过软引用的方式保存外部类的引用。

14 并发问题

15 集合问题

16 线程上下文切换

https://blog.csdn.net/java_xiaoo/article/details/120545731

  • 线程切换,同一进程中的两个线程之间的切换
  • 进程切换,两个进程之间的切换
  • 模式切换,在给定线程中,用户模式和内核模式的切换
  • 地址空间切换,将虚拟内存切换到物理内存
  1. 挂起当前任务(线程/进程),将这个任务在 CPU 中的状态(上下文)存储于内存中的某处
  2. 恢复一个任务(线程/进程),在内存中检索下一个任务的上下文并将其在 CPU 的寄存器中恢复
  3. 跳转到程序计数器所指向的位置(即跳转到任务被中断时的代码行),以恢复该进程在程序中

线程的上下文切换其实就可以分为两种情况:

  1. 两个线程属于不同进程,因为资源不共享,切换过程和进程上线文切换一样
  2. 两个线程属于同一个进程,只需要切换线程的私有数据、寄存器等不共享的数据

16,1 上下文切换额外的开销

上下文切换会导致CPU在寄存器和运行队列之间来回奔波

直接消耗:指的是CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例(翻译后备缓冲区)需要重新加载, CPU 的pipeline需要刷掉
间接消耗:指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小

1 CPU上下文需要保存和加载 寄存器

  • ip(instruction pointer):指向当前执行指令的下一条指令
  • bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
  • sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
  • cr3:页目录基址寄存器,保存页目录表的物理地址

2 系统调度器的代码需要执行
3 TLB实例(翻译后备缓冲区)需要重新加载,
4 CPU 的pipeline需要刷掉
5 多核的cache之间得共享数据 缓存穿透
如果进程始终都在一个CPU上调度还好一些,如果跨CPU的话,之前热起来的TLB、L1、L2、L3因为运行的进程已经变了,所以以局部性原理cache起来的代码、数据也都没有用了,导致新进程穿透到内存的IO会变多。
6 切换页表全局目录(可能会有)
7 切换内核态堆栈(可能有)
从用户态切换到内核态时,因为使用不同的堆栈段,所以需要进行堆栈切换;而从内核态切换到内核态时,使用的都是内核数据段,所以不需要切换堆栈,但是需要修改TSS的esp0

16.2 何时进行切换

1 中断处理 中断分为硬件中断和软件中断,软件中断包括因为IO阻塞、未抢到资源或者用户代码等原因,线程被挂起
2 多任务处理 每个程序都有相应的处理时间片,当前任务的时间片用完之后,系统CPU正常调度下一个任务
3 用户态切换 这种情况下,上下文切换并非一定发生,只在特定操作系统才会发生上下文切换

17 内核态和用户态

—->内核态: CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡. CPU也可以将自己从一个程序切换到另一个程序
—->用户态: 只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU资源可以被其他程序获取

17.1 为什么需要用户态和内核态?

权限和安全
—->由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 :用户态内核态

17.2 何时进行切换

1. 系统调用
这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使 用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户 特别开放的一个中断来实现,例如Linux的int 80h中断。
2. 异常
当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
3. 外围设备的中断
当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会 暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到 内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的

17.3 转化的代价(比线程上下文轻量级)

每个进程都会有两个栈:一个内核态栈和一个用户态栈。当执行int中断执行时就会由用户态,栈转向内核栈。系统调用时需要进行栈的切换。
1 而且内核代码对用户不信任,需要进行额外的检查。
2 系统调用的返回过程有很多额外工作,比如检查是否需要调度等。
3 系统调用一般都需要保存用户程序得上下文(context), 在进入内核得时候需要保存用户态得寄存器,在内核态返回用户态得时候会恢复这些寄存器得内容。这是一个开销的地方。
4 如果需要在不同用户程序间切换的话,那么还要更新cr3寄存器,这样会更换每个程序的虚拟内存到物理内存映射表的地址,也是一个比较高负担的操作。

17.4 过程

系统调用会将CPU从用户态切换到核心态,以便 CPU 访问受到保护的内核内存。
系统调用的过程会发生 CPU 上下文的切换,CPU 寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态运行内核任务。
而系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。所以,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。
注意:系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程

在Linux下,这个异常具体就是调用int $0x80的汇编指令,这条汇编指令将产生向量为0x80的编程异常。

18 I/O 频繁发生内核态和用户态切换

首先要同意这个说法,即I/O会导致系统调用,从而导致内核态和用户态之间的切换。因为对I/O设备的操作是发生在内核态。那如何减少因为I/O导致的系统调用呢?答案是:使用户进程缓冲区。
image.png

19 进程上下文切换

https://cloud.tencent.com/developer/article/1675399

进程上下文切换跟系统调用又有什么区别呢?
1 进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。
2 进程的上下文切换就比系统调用时多了一步:在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、用户栈等保存下来;而加载下一个进程的内核态后,还需要加载这个进程的虚拟内存和用户栈。

19.1、什么时候会切换进程上下文?

只有在进程调度的时候,才需要切换上下文。Linux 为每个 CPU 都维护了一个就绪队列,将活跃进程(即正在运行和正在等待 CPU 的进程)按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。
新进程在什么时候才会被调度到 CPU 上运行呢?
1.运行中的进程执行完终止了,CPU 会释放出来,新的基础进程就可以被调度到CPU上运行了。
2. 运行中的进程时间片用完,进程被挂起
3. 运行中的进程资源不足,进程被挂起
4. 运行中的进程执行Sleep方法主动挂起
5. 新进程优先级更高,运行中的进程被挂起
6. 发生硬件中断,运行中的进程会被中断挂起,转而执行内核中的中断服务程序

19.2 进程上下文代价

首先,进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。
因此,进程的上下文切换就比系统调用时多了一步:在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、用户栈等保存下来

19.2.1 直接开销:

1、切换页表全局目录
2、切换内核态堆栈
3、切换硬件上下文(进程恢复前,必须装入寄存器的数据统称为硬件上下文)

  • ip(instruction pointer):指向当前执行指令的下一条指令
  • bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
  • sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
  • cr3:页目录基址寄存器,保存页目录表的物理地址
  • ……

4、刷新TLB
5、系统调度器的代码执行

19.2.2 间接开销

l1 l2 l3 cache 没有命中,缓存穿透