,其本身是一个虚引用类型。在JDK内部专门用来管理DirectByteBuffer的gc回收时的直接内存清理工作。
其类持有类变量static final ReferenceQueue dummyQueue,及类变量static Cleaner first。其中,dummyQueue只是为了与普通Reference表述一致(在jvm gc中表述一致),其实并不实际承担入列行为。实际的Cleaner对象队列由Cleaner.first静态变量作为链表头统一维护。(也就是说,DirectByteBuffer在构建时通过Cleaner.first链表进行维护,并在gc回收时不走常规的入列ReferenceQueue再从ReferenceQueue中取出处理的流程,而是直接从Cleaner.first队列中删除,并执行其cleaner.thunk.run()逻辑)
另持有对象变量Cleaner next, Cleaner pre及Runnable thunk。每个Cleaner对象通过next, pre构成一个双向链表,其表头为Cleaner.first(全局唯一),并关联一个Runnable thunk,在Cleaner节点对象创建时赋予:对于DirectByteBuffer持有的Cleaner而言,为其注册的Runnable为Deallocator或Runnable unmapper(对于使用mmap方式构建的DirectByteBuffer而言)。
对于Cleaner类型,首先其继承了PhantomReference且在初始化时就注册了ReferenceQueue dummyQueue,因此当jvm的gc在发现其不可达后会将其通过disovered属性链入Reference.pending的链表中。而在ReferenceHandler线程执行tryHandlePending()时,若发现该Reference类型为Cleaner,则直接走快速处理逻辑(fast path),直接调用其cleaner.clean()方法,其中会将该cleaner从Cleaner类维护的链表中删除,并执行其cleaner.thunk.run()。而非像普通Reference对象一样,执行r.queue.enqueue(r)入列到自己注册的queue中。
在JDK8以前,在内部Cleaner类型是为DirectByteBuffer专门使用,为了加快其gc时的表现,因此专门定制了其处理逻辑,走快速通道(参见Reference Handler常驻线程的执行逻辑)。另外,通过Cleaner.create(obj, runnable)方法可以在应用中通过显式调用使用该机制。
在Java 9中,终结方法(Finalizer)已经被遗弃了,但它们仍被Java类库使用,相应用来替代终结方法的是清理方法(cleaner)。比起终结方法,清理方法相对安全点,但仍是不可以预知的,运行慢的,而且一般情况下是不必要的。JDK9中有很多原来使用覆盖Object#finalize()方法的清理工作实现都替换为Cleaner实现了,但是仍然不鼓励使用这种方式。
2.4 Finalizer对象
FinalReference继承自Reference,是内置的用来实现java finalization机制的一种引用类型,并不开放给外部应用程序使用。
Finalizer继承自FinalReference,用于实现普通对象由于实现了finalize()方法而需要在gc时对象被回收前执行一些逻辑的功能。Finalizer机制保证对象的finalize()方法一定会被调用(但不保证执行完成)。
其内部持有类变量static ReferenceQueue queue和static Finalizer unfinalized,其中queue是Finalizer对象作为Reference对象,其本身同样遵守jvm在gc时将其放入pending list中,并由Reference Handler线程将其入列的行为(实际的入列,而非Cleaner的快速处理)。那为何还要维护一个unfinalized队列呢?原来所有的Finalizer对象在创建时都会链入该unfinalized链表中,以记录该对象属于无论如何(就算进程要退出)都需要被调用finalize()方法的对象。因此,专门有一个unfinalized链表用于保存这些对象,并在必要时(例如对象尚未被gc回收,但是进程要退出),遍历该链表并逐个调用其finalize()方法。相对的,被gc标记而入列的Finalizer对象会由常驻的Finalizer线程执行其出列及finalize()方法调用。
另持有实例变量next和prev用于将unfinalized链表构建为一个双向链表(此处的双向链表,是为了方便节点随机删除),还持有一个普通对象锁lock(用于保护unfinalized链表的操作)。
一个Finalizer对象已经被执行了析构(finalized)的标志是next==prev==this,在其被从unfinalized队列中删除时就是其被finalized时,此时会将其next与prev都置为this。
其register(Object finalizee)方法会被jvm调用,当对象初始化时,若jvm判断出其需要被finalize(),则会调用该方法将其封装为一个Finalizer对象并添加到Finalizer.unfinalized双向链表中,并为其注册ReferenceQueue queue:
/* Invoked by VM */
static void register(Object finalizee) {
new Finalizer(finalizee);
}
private Finalizer(Object finalizee) {
super(finalizee, queue);
add();
}
private void add() {
synchronized (lock) {
if (unfinalized != null) {
this.next = unfinalized;
unfinalized.prev = this;
}
unfinalized = this;
}
}
其runFinalization()方法会被Runtime.runFinalization()调用,用于显式调用finalization的行为。其中会在forkSecondaryFinalizer(runnable)方法中构建一个新线程来跑runnable,其run()行为是从queue中逐个poll()出已入列的Finalizer f,并调用其finalize()方法。其中又借助了看上去高大上的SharedSecrets与JavaLangAccess jla,调用的f.runFinalizer(jla)中最终调用的就是jla.invokeFinalizer(r)(其实际逻辑就是r.finalize()),只是f.runFinalizer(jla)中还包含了一些其他逻辑(后面详述)。一旦发现了队列为空则立刻break退出。代码如下:
/* Called by Runtime.runFinalization() */
static void runFinalization() {
if (!VM.isBooted()) {
return;
}
forkSecondaryFinalizer(new Runnable() {
private volatile boolean running;
public void run() {
// in case of recursive call to run()
if (running)
return;
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
Finalizer f = (Finalizer)queue.poll();
if (f == null) break;
f.runFinalizer(jla);
}
}
});
}
其runAllFinalizers()方法会被Shutdown调用,也就是说进程退出之前会调用该方法。其中会在forkSecondaryFinalizer(runnable)中构建一个新线程执行runnable,其run()行为是遍历unfinalized链表,对其中的每一个Finalizer f,调用其finalize()方法,同样通过f.runFinalizer(jla)->最终调用到f.finalize()。一旦发现unfinalized链表为空之后,立刻break退出:
/* Invoked by java.lang.Shutdown */
static void runAllFinalizers() {
if (!VM.isBooted()) {
return;
}
forkSecondaryFinalizer(new Runnable() {
private volatile boolean running;
public void run() {
// in case of recursive call to run()
if (running)
return;
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
Finalizer f;
synchronized (lock) {
f = unfinalized;
if (f == null) break;
unfinalized = f.next;
}
f.runFinalizer(jla);
}}});
}
其私有的runFinalizer(JavaLangAccess jla)方法供runFinalization()与runAllFinalizers()方法调用(还有Finalizer线程),代码如下:
private void runFinalizer(JavaLangAccess jla) {
synchronized (this) {
if (hasBeenFinalized()) return;
remove();
}
try {
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
jla.invokeFinalize(finalizee);
/* Clear stack slot containing this variable, to decrease
the chances of false retention with a conservative GC */
finalizee = null;
}
} catch (Throwable x) { }
super.clear();
}
runFinalizer方法执行逻辑为:
首先判断,若已经被finalized过了(hasBeenFinalized()==true),则直接返回;否则执行remove()从unfinalized队列中删除本Finalizer节点
接着从this中得到要执行finalize()方法的finalizee对象
判断若finalizee不为null,且其类型不为Enum,则调用jla.invokeFinalize(finalizee)->其逻辑就是finalizee.finalize()
最后调用super.clear(),也就是Reference.clear()方法,将this.referent置为null
2.4.1 Finalizer常驻线程
在Finalizer类中声明了一个FinalizerThread,命名为"Finalizer",其run方法逻辑为在无限循环中通过queue.remove()阻塞的获取queue中入列的Finalizer对象f,若得到了则执行其f.runFinalizer(jla)(逻辑如上所述)。该线程捕捉interrupt异常并ignore,因此会永久处于循环状态,也就是说只要java进程未退出,则该线程会一直常驻。
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer(jla);
} catch (InterruptedException x) {
// ignore and continue
}
}
该线程优先级被设置为MAX_PRIORITY-2,并在Finalizer类初始化时在clinit<>()方法中启动:
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
由上所述,在java进程中会存在两个常驻线程:
Reference Handler线程(高优先级),其作用是将处于pending list中的Reference对象执行入列ReferenceQueue,或Cleaner c.clean()
Finalizer线程(低优先级),其作用是执行Finalizer.queue中的被入列(被Reference Handler线程入列)对象的finalize()方法
通过jstack也可以确认每一个java进程确实都有Reference Handler和Finalizer这两个常驻线程:
......
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fabc580b000 nid=0x4503 in Object.wait() [0x0000700008b14000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007b5730518> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000007b5730518> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fabc600c800 nid=0x3103 in Object.wait() [0x0000700008a11000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007b5740688> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000007b5740688> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
3. java各种引用对象垃圾回收行为概述
综合上述可得,JDK结合JVM GC对对象的回收行为可以 分为如下五大类:
普通Object,未实现finalize()方法(强引用对象)
注册了ReferenceQueue的Reference对象(软、弱、虚引用对象)
未注册ReferenceQueue的Reference对象
nio DirectByteBuffer对象(封装为Cleaner进行处理)
实现了finalize()方法的析构对象(封装为Finalizer对象进行处理)
JVM GC判断所有GC Root不可达的对象,并将其中的注册了ReferenceQueue的Reference类型的对象链入pending list中(上文提到的);将其他的状态直接置为Inactive或直接清理。各引用类型对象的垃圾回收行为描述如下:
3.1 普通Object
普通Object在GC Root不可达之后会被直接执行标记(mark),并在清理阶段清理掉,并不具备Reference引用对象的各种状态
3.2 未注册ReferenceQueue的Reference对象
未注册ReferenceQueue的Reference对象,在被gc判断出GC Root不可达之后会被直接设置为Inactive状态
3.3 nio DirectByteBuffer对象
DirectByteBuffer在构建时,jdk内部会为其创建一个Cleaner对象,并注册一个dummyQueue,然后将该cleaner添加到Cleaner.first为head的双向链表中。当gc判断出DirectByteBuffer为GC Root不可达之后会将其对应的Cleaner放入pending list中。而常驻线程Reference Handler在处理Cleaner c时并不会将其入列,而是直接调用c.clean()快速处理方法,其中会从Cleaner.first链中删除该节点,并调用其c.thunk.run()完成clean逻辑
3.4 注册了ReferenceQueue的Reference对象
注册了ReferenceQueue的Reference对象,被gc处理后会被放入pending list中(Reference.pending为head),常驻线程Reference Handler在处理Reference r时,会将其入列自己注册的ReferenceQueue q:r.queue.enqueue(r)。此后,对该ReferenceQueue中对象的监控及后续处理就交给应用自行实现(开放给应用程序来定制,例如netty的ObjectCleaner,及guava的FinalizableReference/FinalizableReferenceQueue都是具体的定制案例)
3.5 实现了finalize()方法的析构对象
实现了finalize()方法的对象,在对象创建时由jvm自行判断出该对象需要执行finalize逻辑,因此将其封装为一个Finalizer对象并为其注册Finalizer.queue队列,然后将其链入Finalizer.unfinalized链表中。当被gc处理之后,会将该Finalizer对象放入pending list中,并由常驻线程Reference Handler放入Finalizer.queue中(入列)。接着入列的对象会由另外一个常驻线程Finalizer逐个处理,执行其finalize()方法中的逻辑(处理之后会从Finalizer.unfinalized队列中删除)。另外在shutdown时,会从Finalizer.unfinalized链表中逐个取出Finalizer f,并执行其f.finalize()逻辑,以确保每一个实现了finalize()方法的对象的finalize()方法都会被调用一次
4. 对Reference的扩展应用
如上对于注册了ReferenceQueue的Reference对象在垃圾回收时的行为说明,其对于入列ReferenceQueue之后对相关引用对象的监控及后续处理就开放给应用自行实现,因此可以通过扩展该功能实现自己的逻辑。
4.1 netty ObjectCleaner
ObjectCleaner是netty提供的用于给对象object注册其被回收时需要被执行逻辑,最初netty会在设置FastThreadLocal对象的值时默认将当前线程注册进ObjectCleaner中,其被gc回收时调用的逻辑是remove(threadLocalMap)。现在对这部分逻辑做了修改,InternalThreadLocalMap threadLocalMap会在FastThreadLocalThread线程中自动管理释放,但是若在普通用户线程中使用netty提供的FastThreadLocal时,就需要自己管理FastThreadLocal数据的清理,此时可以通过ObjectCleaner来注册清理逻辑FastThreadLocal.removeAll(),需要特别注意的是在线程池中线程对象并不会被实际回收,此时注册的对象不应该是线程本身,而应该是一个本线程执行完毕后就会被gc收回的对象,否则仍然会导致FastThreadLocal数据无法释放。
netty的ObjectCleaner实现部分借鉴了jdk的Cleaner及Finalizer的思路,通过显示调用ObjectCleaner.register(Object obj, Runnable cleanupTask)来显式管理对象在被回收时应执行的逻辑。目前netty自身并未使用该方案,由应用自行决定使用。
其自身维护了一个ConcurrentSet LIVE_SET用于通过register方法注册在gc回收时需要执行runnable逻辑的对象。另外定义了自己的ReferenceQueue REFERENCE_QUEUE,并由一个AtomicBoolean CLEANER_RUNNING来标识当前是否有Cleaner线程正在执行。其清理线程Cleaner不像jdk的Finalizer一样常驻,而是通过定义一个Runnable CLEANER_TASK来描述Cleaner线程的动作,并在有必要时(LIVE_SET非空)才会启动Cleaner线程以执行CLEANER_TASK定义的逻辑,代码如下:
public void run() {
boolean interrupted = false;
for (;;) {
// Keep on processing as long as the LIVE_SET is not empty and once it becomes empty
// See if we can let this thread complete.
while (!LIVE_SET.isEmpty()) {
final AutomaticCleanerReference reference;
try {
reference = (AutomaticCleanerReference) REFERENCE_QUEUE.remove(REFERENCE_QUEUE_POLL_TIMEOUT_MS);
} catch (InterruptedException ex) {
// Just consume and move on
interrupted = true;
continue;
}
if (reference != null) {
try {
reference.cleanup();
} catch (Throwable ignored) {
// ignore exceptions, and don't log in case the logger throws an exception, blocks, or has
// other unexpected side effects.
}
LIVE_SET.remove(reference);
}
}
CLEANER_RUNNING.set(false);
// Its important to first access the LIVE_SET and then CLEANER_RUNNING to ensure correct
// behavior in multi-threaded environments.
if (LIVE_SET.isEmpty() || !CLEANER_RUNNING.compareAndSet(false, true)) {
// There was nothing added after we set STARTED to false or some other cleanup Thread
// was started already so its safe to let this Thread complete now.
break;
}
}
if (interrupted) {
// As we caught the InterruptedException above we should mark the Thread as interrupted.
Thread.currentThread().interrupt();
}
}
其执行逻辑为,在无限循环中执行如下:
首先,若LIVE_SET非空,则在循环中逐个从REFERENCE_QUEUE中取出reference(已被Reference Handler入列的),若reference非null,则执行reference.cleanup()方法,并从LIVE_SET中删除该reference。该循环会捕捉各种异常,因此在LIVE_SET非空之前不会退出。
当LIVE_SET为空了,说明上面循环已经将所有加入LIVE_SET中的对象都回收并处理了,此时将CLEANER_RUNNING标志设置为false,标记当前线程处理未执行状态准备退出了。
接着再次判断一下LIVE_SET是否又变得非空了(可能在本线程退出循环之后,又有通过register方法将对象注册进了LIVE_SET),若非空,则cas设置CLEANER_RUNNING为true(原值为false),此处需要cas设置,因为该值有可能会被调用register方法的线程给cas的设置为true,只能有一个线程设置为true成功。
如果LIVE_SET为空,或者cas设置CLEANER_RUNNING没有成功,则直接break打断外层无限循环,标志着本CLEANER_TASK线程执行完毕了
否则,说明LIVE_SET非空,且CLEANER_RUNNING又被本线程cas的设置为true了,则继续循环的处理
其对外提供的static void register(Object obj, Runnable cleanupTask)方法代码如下:
public static void register(Object object, Runnable cleanupTask) {
AutomaticCleanerReference reference = new AutomaticCleanerReference(object,
ObjectUtil.checkNotNull(cleanupTask, "cleanupTask"));
// Its important to add the reference to the LIVE_SET before we access CLEANER_RUNNING to ensure correct
// behavior in multi-threaded environments.
LIVE_SET.add(reference);
// Check if there is already a cleaner running.
if (CLEANER_RUNNING.compareAndSet(false, true)) {
final Thread cleanupThread = new FastThreadLocalThread(CLEANER_TASK);
cleanupThread.setPriority(Thread.MIN_PRIORITY);
// Set to null to ensure we not create classloader leaks by holding a strong reference to the inherited
// classloader.
// See:
// - https://github.com/netty/netty/issues/7290
// - https://bugs.openjdk.java.net/browse/JDK-7008595
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
cleanupThread.setContextClassLoader(null);
return null;
}
});
cleanupThread.setName(CLEANER_THREAD_NAME);
// Mark this as a daemon thread to ensure that we the JVM can exit if this is the only thread that is
// running.
cleanupThread.setDaemon(true);
cleanupThread.start();
}
}
其执行逻辑为:
将obj和该obj被回收时要执行的逻辑cleanupTask封装为一个AutomaticCleanerReference,并将其添加到LIVE_SET中
然后,cas的修改CLEANER_RUNNING标志(在确认其值为false时,将其设置为true,为了确保只有一个CLEANER_TASK线程在执行)
若修改成功,则首先使用Runnable CLEANER_TASK创建一个FastThreadLocalThread并将其优先级设置为最低,接着在特权代码中修改该线程的contextClassLoader为null(防止由于继承的classLoader而导致强引用该classLoader,造成内存泄露),最后启动该CLEANER_TASK线程,线程执行逻辑如上所述
若未修改成功,则说明有CLEANER_TASK正在运行,不需要再次启动该线程
ObjectCleaner内部使用的AutomaticCleanerReference继承了WeakReference,其内部定义了一个Runnable cleanupTask用于保存通过register注册进来的对象obj在被回收之前应该执行的逻辑。
AutomaticCleanerReference实例在初始化时,将参数Object obj构建为一个WeakReference并为其注册队列REFERENCE_QUEUE,并将参数cleanupTask赋予this.cleanupTask。其cleanup()方法就是执行cleanupTask.run(),其重写了WeakReference的get()方法,直接返回null(与直接继承PhantomReference没什么区别)。另外,重写了Reference的clear()方法,其行为除了调用super.clear()方法以外,还从LIVE_SET中删除本对象。
4.2 guava FinalizableReference/FinalizableReferenceQueue
对guava FinalizableReference/FinalizableReferenceQueue的使用及源代码分析请参见另一篇文章《使用ReferenceQueue实现对ClassLoader垃圾回收过程的观察、以及由此引发的ClassLoader内存泄露的场景及排查过程》,此处不再赘述。
5. 总结
Reference/ReferenceQueue机制是与JVM GC紧密协作实现的,用于支持不同强度的引用对象在垃圾回收中的不同行为。另外JDK自身基于较弱的引用(Weak,Phantom)不会影响到被引用对象在gc中的生存周期这一特性,扩展并实现了诸如对象析构方法的支持,以及nio DirectByteBuffer被gc回收时自动内存释放等等功能。此外,一些著名的第三方类库也利用这种特性实现了自己定制的对象回收逻辑处理机制。因此,正确的了解这一机制背后的原理有助于帮助我们在特定的场景中更优雅合理的使用各种引用。
你可能感兴趣的:(源代码分析,java,垃圾回收,Reference,ReferenceQueue,ObjectCleaner)
【Java】代理模式
非 白
代理模式 java 开发语言
代理模式代理模式是指给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问代理模式是一种结构型设计模式背景如果不采用代理,对一个类的多个方法进行监控时,重复的代码总是重复出现,不但破坏了原方法,如果要实现多个监控,将会对代码造成大量冗余。同时,还导致业务代码,与非业务的监控代码掺杂在一起,不利于扩展和维护。代理类在无限制膨胀,就需要无限的修改业务代码。而采用代理后,原方法不需要做任何改动,操
技术分享:MyBatis SQL 日志解析脚本
£漫步 云端彡
运维趣分享 sql java mybatis 日志解析
技术分享:MyBatisSQL日志解析脚本1.脚本功能概述2.实现细节2.1HTML结构2.2JavaScript逻辑3.脚本代码4.使用方法4.1示例5.总结在日常开发中,使用MyBatis作为持久层框架时,我们经常需要查看SQL日志以调试和优化查询。然而,MyBatis的日志输出通常包含占位符和参数信息,这使得直接执行这些SQL语句变得困难。为了解决这个问题,我们开发了一个简单的HTML和Ja
Spring Bean 生命周期的执行流程
涛粒子
spring 数据库 java
1.Bean定义阶段在Spring应用启动时,会读取配置文件(如XML配置、Java注解配置等)或者扫描带有特定注解(如@Component、@Service、@Repository等)的类,将这些Bean的定义信息加载到Spring的BeanFactory或ApplicationContext中。这些定义信息包括Bean的类名、作用域、依赖关系等。2.Bean实例化阶段调用构造函数:Spring
Spring Bean 生命周期的执行流程
涛粒子
spring java 后端
1.Bean定义阶段解析配置元数据:Spring容器会读取配置信息,这些配置信息可以是XML文件、Java注解或者Java配置类。容器根据这些配置信息解析出Bean的定义,包括Bean的类名、作用域、依赖关系等。注册Bean定义:解析完成后,Spring会将Bean定义信息注册到BeanDefinitionRegistry中,BeanDefinitionRegistry是一个存储Bean定义的注册
使用Druid连接池优化Spring Boot应用中的数据库连接
和烨
其它 spring boot 数据库 后端
使用Druid连接池优化SpringBoot应用中的数据库连接使用Druid连接池优化SpringBoot应用中的数据库连接1.什么是Druid连接池?2.在SpringBoot中配置Druid连接池2.1添加依赖2.2配置Druid连接池2.3配置参数详解3.启用Druid监控4.总结使用Druid连接池优化SpringBoot应用中的数据库连接在现代的Java应用中,数据库连接管理是一个非常重
jvm虚拟机详解(一)-----jvm概述
Mir Su
JVM由浅至深 jvm java
写在前面本篇文章是再下人生中的第一次发布关于技术相关的文章。从事开发工作这么多年来,也算是对自己过往的工作的一个总结,对人生的一次重装再出发。从jvm谈起,然后是关于mysql、redis、消息中间件、微服务等最后在归纳一些常见的java面试方面的高频问题。这是开始我的一个写博计划,希望感兴趣的朋友加个关注一起探讨,有什么不做的地方也请欢迎指教。为什么要先说jvm呢?因为jvm是java程序蜕变的
java新技术
计算机毕业设计系统
转载:http://lj6684.iteye.com/blog/895010最近在网上查资料碰到好多没接触过的技术,先汇总在这里备用,以后慢慢吸收1.JNAJNI的替代品,调用方式比JNI更直接,不再需要JNI那层中间接口,几乎达到Java直接调用动态库2.SmallSQL基于JDBC3.0转为Desktop应用设计的嵌入式数据库,纯Java,本地访问,不支持网络但目前好像不太活跃,最新版本是0.
Java 与设计模式(15):模板方法模式
暗星涌动
设计模式 java 设计模式 模板方法模式 spring boot
一、定义模板方法模式是一种行为设计模式,它定义了一个操作中的算法的骨架(也就是大致的步骤和流程),而将一些具体步骤的实现延迟到子类中。这样,子类可以不改变算法的结构即可重新定义算法的某些特定步骤。二、Java示例举个简单的例子:假设我们要泡一杯茶和一杯咖啡,这两者的制作过程有一些共同的步骤,比如烧水、倒水、搅拌等,但也有不同的地方,比如茶需要放茶叶,而咖啡需要放咖啡粉。泡茶的过程:烧水、放茶叶、倒
js的垃圾回收机制
www.www
JavaScript 相关 javascript 前端 开发语言
js中的垃圾回收机制JavaScript作为一种高级语言,开发者不需要手动管理内存的分配和释放。垃圾回收机制是JavaScript引擎中的一部分,负责自动回收那些不再被使用的内存,确保内存资源得到有效利用,避免内存泄漏。垃圾回收机制主要有两种算法:引用计数和标记清除引用计数基本原理:每个对象都有一个引用计数器,当有一个引用指向该对象时,计数器+1,当一个引用不再指向该对象时,计数器-1。如果某个对
若依前后端分离集成CAS详细教程
Roc-xb
单点登录 前后端分离 CAS
目录一、后端配置1、添加cas依赖2、修改配置文件3、修改LoginUser.java4、修改Constants.java5、添加CasProperties.java6、添加CasUserDetailsService.java7、添加CasAuthenticationSuccessHandler.java8、修改SecurityConfig9、启动后端二、前端配置1、修改settings.js2、
前后端分离跨域问题解决方案
慕容屠苏
大前端爬坑之路 前后端分离 跨域问题解决方案
前后端分离跨域问题解决方案现在的web开发中经常会用到前后分离技术,前后端分解技术,都会涉及到跨域问题。解决跨域问题的方法:第一种解决方案jsonp(不推荐使用)这种方案其实我是不赞同的,第一,在编码上jsonp会单独因为回调的关系,在传入传出还有定义回调函数上都会有编码的”不整洁”.简单阐述jsonp能够跨域是因为javascript的script标签,通过服务器返回script标签的code,
lombok 不生效
howeres
Maven maven
Lombok不生效0现象在build/rebuild时,提示Lombok不生效:java:Youaren’tusingacompilersupportedbylombok,solombokwillnotworkandhasbeendisabled.或java:JPSincrementalannotationprocessingisdisabled.Compilationresultsonparti
CSS flex布局 列表单个元素点击 本行下插入详情独占一行
Cxiaomu
CSS3 UI设计 css 前端
技术栈:Vue2+javaScript简介在实际开发过程中有遇到一个场景:一个list,每行个数固定,点击单个元素后,在当前行与下一行之间插入一行元素详情,便于更直观的查看到对应的数据详情。这种情形,在移动端比较常见,比如用户列表,点击单个列表展示详情,可以考虑flex布局+positionrelative定位。实现思路对于需求重点和实现拆解列表元素:for遍历每行固定(3)个元素:flex布局、
20个高级Java开发面试题及答案!
Java进阶八股文
java jvm 开发语言 spring 面试 spring boot
1、java中都有哪些引用类型?(1)强引用Java中默认声明的就是强引用,比如:Objectobj=newObject();obj=null;只要强引用存在,垃圾回收器将永远不会回收被引用的对象。如果想被回收,可以将对象置为null;(2)软引用(SoftReference)在内存足够的时候,软引用不会被回收,只有在内存不足时,系统才会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,
同城拼车打车约车系统:Java源码全开源构建与优化
狂团商城小师妹
博纳miui52086 微信小程序 小程序 微信公众平台
同城拼车系统是一个复杂且功能全面的软件系统,它巧妙地运用互联网技术,将具有相同出行需求的乘客与车主进行精准匹配,旨在实现资源的最大化共享、显著降低出行成本、有效缓解交通拥堵问题,并大幅提升出行效率。Java,作为一种功能强大、应用广泛的编程语言,凭借其出色的跨平台性、丰富的API库以及强大的性能,成为开发此类系统的理想选择。一、Java源码构建系统架构MVC架构:同城拼车系统采用MVC(Model
活动报名系统源码:JAVA同城服务系统活动报名同城圈子商家商城城市代理躲猫猫
狂团商城小师妹
博纳miui52086 java 人工智能 大数据 微信公众平台 微信小程序
JAVA同城服务系统:打造多元化社交与娱乐新体验在数字化时代,同城服务系统已成为连接城市生活的重要桥梁。我们精心打造的JAVA同城服务系统,不仅融合了活动报名、同城圈子、商家商城、城市代理等多重功能,还特别加入了创新的“躲猫猫”游戏模块,旨在为用户提供一个集社交、娱乐、消费于一体的综合性平台。以下是对该系统功能的详细介绍及技术栈分析。功能介绍活动报名用户可以通过系统轻松浏览并参与同城各类精彩活动,
Java如何调用构造函数和方法以及使用
WZMeiei
java 开发语言
调用构造函数的格式构造函数在创建新对象时被调用。调用格式如下:ClassNameobjectName=newClassName(parameters);ClassName:你需要创建其实例的类的名称。objectName:你将创建的对象的名称。parameters:如果你使用的是带有参数的构造函数,这里需要传递相应的参数。示例:Personperson=newPerson("John",25);调
Java中的static关键字
WZMeiei
Java java 开发语言
static是Java中的一个关键字,主要用于修饰类成员(变量和方法),以表示这个成员属于类本身,而不是类的实例1.静态变量(StaticVariables)类级属性:静态变量也称为类变量或静态属性,它们在类加载时初始化,并且只有一份拷贝,被所有该类的对象共享。这意味着无论创建多少个对象,静态变量的内存空间只有一处。生命周期长:静态变量的生命周期与类相同,只要应用运行,它们就存在。访问方式:可以直
AJAX使用和固定格式
乐多_L
ajax 前端 javascript
ajax的全称AsynchronousJavaScriptandXML(异步JavaScript和XML)。ajax是一种创建交互式网页应用的网页开发技术。其中最核心的依赖是浏览器提供的XMLHttpRequest对象,是这个对象使得浏览器可以发出HTTP请求与接收HTTP响应。实现了在页面不刷新的情况下和服务器进行交互。方法描述newXMLHttpRequest()生成一个XMLHttpRequ
JavaScript的内置对象有哪些?
乐多_L
javascript 开发语言 ecmascript
一、内置对象1、概念JavaScript中的对象共分为3种:自定义对象、浏览器对象和内置对象。之前我们自己创建的对象都属于自定义对象,而内置对象又称为API,是指JavaScript语言自己封装的一些对象,用来提供一些常用的基本功能,来帮助我们提高开发速度,例如:数学-Math、日期-Date、数组-Array、字符串-String等等。JavaScript的内置对象很多,我们不可能都记住,所以我
基于java新闻管理系统,推荐一款开源cms内容管理系统ruoyi-fast-cms
xnqys
java 开源 java 开源 开发语言
一、项目概述1.1项目背景在信息高速流通的当下,新闻媒体行业每天都要处理和传播海量信息。传统的新闻管理模式依赖人工操作,在新闻采集、编辑、发布以及后续管理等环节中,不仅效率低下,而且容易出现人为失误。同时,面对用户日益多样化的信息获取需求,传统方式也难以实现个性化、精准化的内容推送。而Java语言凭借其跨平台性、安全性、稳定性以及丰富的类库和强大的开发框架,成为开发新闻管理系统的理想选择。通过基于
lombok在高版本idea中注解不生效的解决
L_!!!
springboot maven java 服务器 前端
环境:IntelliJIDEA2024.3.1.1+SpringBoot+Maven问题描述使用@AllArgsConstructor注解一个用户类,然后调用全参构造方法创建对象,出现错误:java:无法将类com.itheima.pojo.User中的构造器User应用到给定类型; 需要:没有参数 找到: java.lang.Integer,java.lang.String,java.lang
《JavaScript高级程序设计》——第四章:变量、作用域与内存管理
dorabighead
javascript 开发语言 ecmascript
《JavaScript高级程序设计》——第四章:变量、作用域与内存管理大家好!我是小哆啦,欢迎回到《JavaScript高级程序设计》的读书笔记大本营!在这章中,我们要聊的是两个让人头疼又迷人的话题——变量、作用域与内存管理。有些人一提到这些,就会感到一阵头晕目眩,恍若置身一场JavaScript版的迷宫大冒险!但今天,小哆啦会带你们轻松过关,深入了解这些概念,并且保持足够的幽默感,让你既能笑着学
Java——列表(List)
不会Hello World的小苗
Java java list python
概述在Java中,列表(List)是一种有序的集合,它允许元素重复,并且每个元素都有一个对应的索引值。Java提供了List接口及其实现类,用于表示和操作列表数据。常用的实现类包括ArrayList、LinkedList和Vector。1、List接口概述List是Java集合框架中的一种接口,继承自Collection接口。它定义了许多常见的操作,如:添加元素:add(Ee)、add(intin
Java 中的包(Package)与导入(Import)详解
小刘|
java 开发语言
目录一、引言二、包的概念(一)包的定义与作用(二)JDK中主要的包三、导入的概念(一)导入的目的与用法(二)特殊情况的导入四、补充知识点(一)静态导入(二)包的访问权限(三)包的命名规范五、总结一、引言在Java编程中,包(Package)和导入(Import)是非常重要的概念。它们帮助我们更好地组织代码、管理项目结构、解决命名冲突以及控制访问权限。本文将详细介绍Java中的包和导入的相关知识,通
基于JavaSpringboot+Vue实现前后端分离房屋租赁系统
网顺技术团队
成品程序项目 vue.js 前端 javascript 课程设计 spring boot mybatis
基于JavaSpringboot+Vue实现前后端分离房屋租赁系统作者主页网顺技术团队欢迎点赞收藏⭐留言文末获取源码联系方式查看下方微信号获取联系方式承接各种定制系统精彩系列推荐精彩专栏推荐订阅不然下次找不到哟Java毕设项目精品实战案例《1000套》感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人文章目录基于JavaSpringboot+
Python学习心得两大编程思想
lifegoesonwjl
python 开发语言 pycharm 前端 c语言
一、两大编程思想:1.面向过程:功能上的封装典型代表:C语言2.面向对象:属性和行为上的封装典型代表:Python、Java二、面向过程与面向对象的异同点:1.区别:面向过程:事物比较简单,可用线性的思维去解决面向对象:事务比较复杂,使用简单的线性思维无法解决2.共同点:(1)面向过程和面向对象都是解决实际问题的一种思维方式;(2)二者相辅相成,并不是对立的;(3)解决复杂问题,通过面向对象方式便
java实现,使用向量相似度 输入字符串,在定义好的字符串集合中根据语义匹配出最准的一个。
melck
1024程序员节
以下是完整的Java示例代码,包括字符串集合的定义和根据输入字符串匹配最相似字符串的逻辑:importjava.util.*;publicclassSemanticMatching{publicstaticvoidmain(String[]args){//定义字符串集合ListstringCollection=Arrays.asList("Whereistherestroom?","Canyout
java 实现TextRank算法提取文章摘要
melck
java 算法 开发语言
在Java中,常用的文章摘要提取库是“TextRank”算法。该算法从文本中提取主题和段落,并根据主题和文本中的单词计算权重。使用TextRank实现文章摘要提取具体步骤如下:寻找文章中的关键句子:首先需要分割出文章中的句子,可以使用分词库将文章拆分成句子,然后使用TextRank算法找到文章中与主题相关的句子,这些句子通常包含有标题、关键字等。计算句子的权重:针对关键句子,需要对每个句子计算权重
简易java调用DeepSeek Api教程
m0_62519278
学习小本本 java 数据库 开发语言
一、请求格式首先观察官方文档给出的访问api的样例脚本curlhttps://api.deepseek.com/chat/completions\-H"Content-Type:application/json"\-H"Authorization:Bearer"\-d'{"model":"deepseek-chat","messages":[{"role":"system","content":"
二分查找排序算法
周凡杨
java 二分查找 排序 算法 折半
一:概念 二分查找又称
折半查找(
折半搜索/
二分搜索),优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而 查找频繁的有序列表。首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表 分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步
java中的BigDecimal
bijian1013
java BigDecimal
在项目开发过程中出现精度丢失问题,查资料用BigDecimal解决,并发现如下这篇BigDecimal的解决问题的思路和方法很值得学习,特转载。
原文地址:http://blog.csdn.net/ugg/article/de
Shell echo命令详解
daizj
echo shell
Shell echo命令
Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:
echo string
您可以使用echo实现更复杂的输出格式控制。 1.显示普通字符串:
echo "It is a test"
这里的双引号完全可以省略,以下命令与上面实例效果一致:
echo Itis a test 2.显示转义
Oracle DBA 简单操作
周凡杨
oracle dba sql
--执行次数多的SQL
select sql_text,executions from (
select sql_text,executions from v$sqlarea order by executions desc
) where rownum<81;
&nb
画图重绘
朱辉辉33
游戏
我第一次接触重绘是编写五子棋小游戏的时候,因为游戏里的棋盘是用线绘制的,而这些东西并不在系统自带的重绘里,所以在移动窗体时,棋盘并不会重绘出来。所以我们要重写系统的重绘方法。
在重写系统重绘方法时,我们要注意一定要调用父类的重绘方法,即加上super.paint(g),因为如果不调用父类的重绘方式,重写后会把父类的重绘覆盖掉,而父类的重绘方法是绘制画布,这样就导致我们
线程之初体验
西蜀石兰
线程
一直觉得多线程是学Java的一个分水岭,懂多线程才算入门。
之前看《编程思想》的多线程章节,看的云里雾里,知道线程类有哪几个方法,却依旧不知道线程到底是什么?书上都写线程是进程的模块,共享线程的资源,可是这跟多线程编程有毛线的关系,呜呜。。。
线程其实也是用户自定义的任务,不要过多的强调线程的属性,而忽略了线程最基本的属性。
你可以在线程类的run()方法中定义自己的任务,就跟正常的Ja
linux集群互相免登陆配置
林鹤霄
linux
配置ssh免登陆
1、生成秘钥和公钥 ssh-keygen -t rsa
2、提示让你输入,什么都不输,三次回车之后会在~下面的.ssh文件夹中多出两个文件id_rsa 和 id_rsa.pub
其中id_rsa为秘钥,id_rsa.pub为公钥,使用公钥加密的数据只有私钥才能对这些数据解密 c
mysql : Lock wait timeout exceeded; try restarting transaction
aigo
mysql
原文:http://www.cnblogs.com/freeliver54/archive/2010/09/30/1839042.html
原因是你使用的InnoDB 表类型的时候,
默认参数:innodb_lock_wait_timeout设置锁等待的时间是50s,
因为有的锁等待超过了这个时间,所以抱错.
你可以把这个时间加长,或者优化存储
Socket编程 基本的聊天实现。
alleni123
socket
public class Server
{
//用来存储所有连接上来的客户
private List<ServerThread> clients;
public static void main(String[] args)
{
Server s = new Server();
s.startServer(9988);
}
publi
多线程监听器事件模式(一个简单的例子)
百合不是茶
线程 监听模式
多线程的事件监听器模式
监听器时间模式经常与多线程使用,在多线程中如何知道我的线程正在执行那什么内容,可以通过时间监听器模式得到
创建多线程的事件监听器模式 思路:
1, 创建线程并启动,在创建线程的位置设置一个标记
2,创建队
spring InitializingBean接口
bijian1013
java spring
spring的事务的TransactionTemplate,其源码如下:
public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean{
...
}
TransactionTemplate继承了DefaultT
Oracle中询表的权限被授予给了哪些用户
bijian1013
oracle 数据库 权限
Oracle查询表将权限赋给了哪些用户的SQL,以备查用。
select t.table_name as "表名",
t.grantee as "被授权的属组",
t.owner as "对象所在的属组"
【Struts2五】Struts2 参数传值
bit1129
struts2
Struts2中参数传值的3种情况
1.请求参数绑定到Action的实例字段上
2.Action将值传递到转发的视图上
3.Action将值传递到重定向的视图上
一、请求参数绑定到Action的实例字段上以及Action将值传递到转发的视图上
Struts可以自动将请求URL中的请求参数或者表单提交的参数绑定到Action定义的实例字段上,绑定的规则使用ognl表达式语言
【Kafka十四】关于auto.offset.reset[Q/A]
bit1129
kafka
I got serveral questions about auto.offset.reset. This configuration parameter governs how consumer read the message from Kafka when there is no initial offset in ZooKeeper or
nginx gzip压缩配置
ronin47
nginx gzip 压缩范例
nginx gzip压缩配置 更多
0
nginx
gzip
配置
随着nginx的发展,越来越多的网站使用nginx,因此nginx的优化变得越来越重要,今天我们来看看nginx的gzip压缩到底是怎么压缩的呢?
gzip(GNU-ZIP)是一种压缩技术。经过gzip压缩后页面大小可以变为原来的30%甚至更小,这样,用
java-13.输入一个单向链表,输出该链表中倒数第 k 个节点
bylijinnan
java
two cursors.
Make the first cursor go K steps first.
/*
* 第 13 题:题目:输入一个单向链表,输出该链表中倒数第 k 个节点
*/
public void displayKthItemsBackWard(ListNode head,int k){
ListNode p1=head,p2=head;
Spring源码学习-JdbcTemplate queryForObject
bylijinnan
java spring
JdbcTemplate中有两个可能会混淆的queryForObject方法:
1.
Object queryForObject(String sql, Object[] args, Class requiredType)
2.
Object queryForObject(String sql, Object[] args, RowMapper rowMapper)
第1个方法是只查
[冰川时代]在冰川时代,我们需要什么样的技术?
comsci
技术
看美国那边的气候情况....我有个感觉...是不是要进入小冰期了?
那么在小冰期里面...我们的户外活动肯定会出现很多问题...在室内呆着的情况会非常多...怎么在室内呆着而不发闷...怎么用最低的电力保证室内的温度.....这都需要技术手段...
&nb
js 获取浏览器型号
cuityang
js 浏览器
根据浏览器获取iphone和apk的下载地址
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" content="text/html"/>
<meta name=
C# socks5详解 转
dalan_123
socket C#
http://www.cnblogs.com/zhujiechang/archive/2008/10/21/1316308.html 这里主要讲的是用.NET实现基于Socket5下面的代理协议进行客户端的通讯,Socket4的实现是类似的,注意的事,这里不是讲用C#实现一个代理服务器,因为实现一个代理服务器需要实现很多协议,头大,而且现在市面上有很多现成的代理服务器用,性能又好,
运维 Centos问题汇总
dcj3sjt126com
云主机
一、sh 脚本不执行的原因
sh脚本不执行的原因 只有2个
1.权限不够
2.sh脚本里路径没写完整。
二、解决You have new mail in /var/spool/mail/root
修改/usr/share/logwatch/default.conf/logwatch.conf配置文件
MailTo =
MailFrom
三、查询连接数
Yii防注入攻击笔记
dcj3sjt126com
sql WEB安全 yii
网站表单有注入漏洞须对所有用户输入的内容进行个过滤和检查,可以使用正则表达式或者直接输入字符判断,大部分是只允许输入字母和数字的,其它字符度不允许;对于内容复杂表单的内容,应该对html和script的符号进行转义替换:尤其是<,>,',"",&这几个符号 这里有个转义对照表:
http://blog.csdn.net/xinzhu1990/articl
MongoDB简介[一]
eksliang
mongodb MongoDB简介
MongoDB简介
转载请出自出处:http://eksliang.iteye.com/blog/2173288 1.1易于使用
MongoDB是一个面向文档的数据库,而不是关系型数据库。与关系型数据库相比,面向文档的数据库不再有行的概念,取而代之的是更为灵活的“文档”模型。
另外,不
zookeeper windows 入门安装和测试
greemranqq
zookeeper 安装 分布式
一、序言
以下是我对zookeeper 的一些理解: zookeeper 作为一个服务注册信息存储的管理工具,好吧,这样说得很抽象,我们举个“栗子”。
栗子1号:
假设我是一家KTV的老板,我同时拥有5家KTV,我肯定得时刻监视
Spring之使用事务缘由(2-注解实现)
ihuning
spring
Spring事务注解实现
1. 依赖包:
1.1 spring包:
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.
iOS App Launch Option
啸笑天
option
iOS 程序启动时总会调用application:didFinishLaunchingWithOptions:,其中第二个参数launchOptions为NSDictionary类型的对象,里面存储有此程序启动的原因。
launchOptions中的可能键值见UIApplication Class Reference的Launch Options Keys节 。
1、若用户直接
jdk与jre的区别(_)
macroli
java jvm jdk
简单的说JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境。SDK是Software Development Kit 一般指软件开发包,可以包括函数库、编译程序等。
JDK就是Java Development Kit JRE是Java Runtime Enviroment是指Java的运行环境,是面向Java程序的使用者,而不是开发者。 如果安装了JDK,会发同你
Updates were rejected because the tip of your current branch is behind
qiaolevip
学习永无止境 每天进步一点点 众观千象 git
$ git push joe prod-2295-1
To git@git.dianrong.com:joe.le/dr-frontend.git
! [rejected] prod-2295-1 -> prod-2295-1 (non-fast-forward)
error: failed to push some refs to 'git@git.dianron
[一起学Hive]之十四-Hive的元数据表结构详解
superlxw1234
hive hive元数据结构
关键字:Hive元数据、Hive元数据表结构
之前在 “[一起学Hive]之一–Hive概述,Hive是什么”中介绍过,Hive自己维护了一套元数据,用户通过HQL查询时候,Hive首先需要结合元数据,将HQL翻译成MapReduce去执行。
本文介绍一下Hive元数据中重要的一些表结构及用途,以Hive0.13为例。
文章最后面,会以一个示例来全面了解一下,
Spring 3.2.14,4.1.7,4.2.RC2发布
wiselyman
Spring 3
Spring 3.2.14、4.1.7及4.2.RC2于6月30日发布。
其中Spring 3.2.1是一个维护版本(维护周期到2016-12-31截止),后续会继续根据需求和bug发布维护版本。此时,Spring官方强烈建议升级Spring框架至4.1.7 或者将要发布的4.2 。
其中Spring 4.1.7主要包含这些更新内容。