Java知识点随记

Java垃圾回收和JVM调优

Java为什么需要进行垃圾回收?

垃圾回收与内存息息相关,垃圾回收能够释放更多空余内存,提高程序的执行效率。

垃圾回收会造成什么问题?

会造成内存资源被gc线程暂用,程序出现卡顿,不可用的情况。

内存调优的目的

通过调节内存大小来平衡GC频率和单次GC的时长。

发现垃圾
  • 引用计数

  • 可达性分析(Java就是用这个算法)

确定哪些对象是不能回收的(也就是GC Root,例如string),然后从这些对象开始逐级往下搜索,不可通达的对象就表示可以回收了。
由判断是否可回收又牵扯到强引用、软引用、弱引用、虚引用的知识了。强引用GC不可回收,软引用内存不够则回收,弱引用一旦GC就回收,虚引用可以用来监听对象是否回收。

垃圾回收算法

发现垃圾以后就要进行垃圾回收了,而要进行垃圾回收,则需要一个垃圾算法:

  • 标记清除算法

当我们进行垃圾回收的时候,会发现内存中的垃圾是不连续的,情况如图:
在这里插入图片描述
黑色部分表示是需要回收的垃圾快,这就是内存碎片。这时候如果我们想申请一块连续的较大内存就有可能造成内存溢出。

  • 复制算法

为了解决内存碎片的问题,我们可以把内存分为两部分,如图:
在这里插入图片描述
左边2、4这部分是我们需要回收的垃圾,我们先把1、3、5复制到右边,然后把左边的全部一次性清除就好,这就是复制算法。但是这样又会造成内存被对半砍的问题,造成资源浪费。

  • 标记整理算法(这就是Java采用的算法)

该算法会把内存分为新生代(伊甸园区、生存区)、老年代,如图:
在这里插入图片描述
在每个区域都要进行GC,存活下来的对象就往后移动,最终进入老年区。

堆栈
  • 内存中的堆栈和数据结构中的栈是两码事,两者没有必然联系
  • 内存中的栈是存放方法和局部变量的,先进后出,用完即释放
  • 内存中的堆,是堆放杂乱无章的数据
多线程
wait、notify、notifyall
  • 这三者是用来做线程间通信的,他们共同持有一个object对象,object.wait()以后会释放锁,别的线程可以继续操作object,直到有其他线程调用object.notify(),wait线程才继续往下执行。
  • 这三者必须要被synchronized(object){}包裹
线程池复用伪代码,不知道理解得对不对,欢迎指教
package com.example.mytest;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

@Slf4j
public class MyThreadPool {

    public static void main(String[] args) {
        //整个过程只创建了一个worker线程
        Worker worker = new Worker();
        worker.start();
        
        //模拟创建无数任务
        for (;;){
            worker.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("线程复用:{}", Thread.currentThread().getName());
                }
            });
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class Worker extends Thread{

        private BlockingDeque<Runnable> workers = new LinkedBlockingDeque<>();

        public void execute(Runnable task) {
            workers.add(task);
            log.info("正在复用的线程:{}", Thread.currentThread().getName());
        }

        @Override
        public void run() {
            while(true){
                Runnable task = workers.poll();
                if(task != null){
                    task.run();//并没有执行start,所以并没有创建线程
                }
            }
        }
    }
}
什么是线程安全?

多个线程操作同一个资源,能够保证数据不被污染就是线程安全,否则就不是。
保证线程安全的办法就是加锁,例如StringBuffer内部就是用了synchronize锁才保证了线程安全。
说到线程安全就不得不提起java内存模型JMM,即java内存分为主内存和工作内存,validate的作用就是实时的把线程的工作内存里的数据刷新到主存。
在这里插入图片描述

代理与反射
  • 代理 就是在不改变原有代码的基础上新增功能,动态代理要继承自InvocationHandler
public class ProxyHandler implements InvocationHandler {

    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(),  target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("代理成功1111");
        Object invoke = method.invoke(target, args);
        log.info("代理成功222222");
        return invoke;
    }
}

其中代理类OSSClient是继承自OSS接口(必须)

@Test
    public void proxyTest(){
        OSSClient ossClient = new OSSClient("11111", "11111");
        ProxyHandler proxyHandler = new ProxyHandler();
        proxyHandler.setTarget(ossClient);
        OSS proxy = (OSS)proxyHandler.getProxy();
        log.info(proxy.getConnectionPoolStats());
    }
  • 反射,反射可以调用其他第三方提供的SDK里的私有方法,这就很有用了
@Test
    public void invokeTest(){
        try {
            Class<?> aClass = Class.forName("com.aliyun.oss.OSSClient");
            Constructor<?> constructor = aClass.getConstructor(String.class, String.class);
            Object newInstance = constructor.newInstance("11111", "11111");
            Method toURI = aClass.getDeclaredMethod("toURI", String.class);
            toURI.setAccessible(true);
            URI invoke = (URI)toURI.invoke(newInstance, "https://www.qq.com");
            log.info("反射结果:{}", invoke.getHost());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
RabbitMQ
Exchange交换机类型
  • topic是路由模式,根据routingkey去分发消息,用 . 做路由分割,*表示一个单词,#表示多个单词
  • fanout是广播模式,在此模式下路由失效,所有订阅的队列都能收到消息
  • direct会把消息发送到routingKey和queue名称一样的队列,例如队列的名字叫queue.test1,那routingKey也必须是queue.test1,队列才能收到消息
死信队列

死信队列产生的原因:

  1. 消息被nack或者被reject,且requeue为false
  2. 消息TTL过期,可以在创建队列的时候规定超时时间,也可以发送消息的时候规定
  3. 队列超出长度限制
    死信队列
    死信队列的创建方式:

哪个正常业务队列需要处理死信消息就在该队列上配置
x-dead-letter-exchange: 死信交换机(其实就是个正常交换机) x-dead-letter-routing-key: 死信队列(其实就是个队列)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值