ความแตกต่างที่แท้จริงระหว่าง“ java -server” และ“ java -client”?


394

มีความแตกต่างในทางปฏิบัติระหว่าง "java -server" และ "java -client" หรือไม่?

ทั้งหมดที่ฉันสามารถหาได้บนเว็บไซต์ของ Sun นั้นค่อนข้างคลุมเครือ

"- เซิร์ฟเวอร์เริ่มช้าลง แต่ควรทำงานได้เร็วขึ้น"

ความแตกต่างที่แท้จริงคืออะไร? (ขณะนี้ใช้ JDK 1.6.0_07)

คำตอบ:


368

นี้จะเชื่อมโยงจริงๆHotSpotและเริ่มต้นค่าตัวเลือก ( Java HotSpot VM ตัวเลือก ) ซึ่งแตกต่างกันระหว่างลูกค้าและการกำหนดค่าเซิร์ฟเวอร์

จากบทที่ 2ของเอกสารทางเทคนิค ( สถาปัตยกรรม Java HotSpot Performance Engine ):

JDK มีสองรสชาติของ VM - ข้อเสนอฝั่งไคลเอ็นต์และ VM ปรับสำหรับแอปพลิเคชันเซิร์ฟเวอร์ โซลูชันทั้งสองนี้ใช้ฐานรหัสสภาพแวดล้อมรันไทม์ Java HotSpot แต่ใช้คอมไพเลอร์ที่แตกต่างกันซึ่งเหมาะสมกับคุณลักษณะด้านประสิทธิภาพที่เป็นเอกลักษณ์ของลูกค้าและเซิร์ฟเวอร์ ความแตกต่างเหล่านี้รวมถึงนโยบายการรวบรวมอินไลน์และค่าเริ่มต้นของฮีป

แม้ว่า Server และ Client VM จะคล้ายกัน แต่ Server VM นั้นได้รับการปรับแต่งเป็นพิเศษเพื่อเพิ่มความเร็วในการทำงานสูงสุด มันมีไว้สำหรับการรันแอพพลิเคชั่นเซิร์ฟเวอร์ที่รันมายาวนานซึ่งต้องการความเร็วในการทำงานที่เร็วที่สุดมากกว่าเวลาเริ่มต้นทำงานที่รวดเร็ว

คอมไพเลอร์ Client VM ทำหน้าที่เป็นการอัพเกรดสำหรับทั้ง Classic Classic และคอมไพเลอร์ just-in-time (JIT) ที่ใช้โดย JDK รุ่นก่อนหน้า ไคลเอนต์ VM เสนอประสิทธิภาพการทำงานขณะปรับปรุงสำหรับแอปพลิเคชันและแอปเพล็ต Java HotSpot Client VM ได้รับการปรับแต่งเป็นพิเศษเพื่อลดเวลาเริ่มต้นแอปพลิเคชันและการปล่อยหน่วยความจำทำให้เหมาะอย่างยิ่งสำหรับสภาพแวดล้อมของไคลเอ็นต์ โดยทั่วไประบบไคลเอนต์จะดีกว่าสำหรับ GUI

ดังนั้นความแตกต่างที่แท้จริงก็เหมือนกันในระดับคอมไพเลอร์:

คอมไพเลอร์ VM ไคลเอนต์ไม่พยายามที่จะดำเนินการเพิ่มประสิทธิภาพที่ซับซ้อนมากขึ้นโดยคอมไพเลอร์ใน Server VM แต่ในการแลกเปลี่ยนมันต้องใช้เวลาน้อยลงในการวิเคราะห์และรวบรวมชิ้นส่วนของรหัส ซึ่งหมายความว่าไคลเอ็นต์ VM สามารถเริ่มทำงานได้เร็วขึ้นและต้องการหน่วยความจำขนาดเล็กลง

Server VM มีคอมไพเลอร์แบบปรับตัวขั้นสูงที่รองรับการเพิ่มประสิทธิภาพประเภทเดียวกันหลายอย่างที่ดำเนินการโดยการปรับแต่งคอมไพเลอร์ C ++ รวมถึงการปรับแต่งบางอย่างที่ไม่สามารถทำได้โดยคอมไพเลอร์แบบดั้งเดิมเช่นการแทรกซึมเชิงรุก นี่เป็นข้อได้เปรียบด้านการแข่งขันและประสิทธิภาพเหนือคอมไพเลอร์แบบคงที่ เทคโนโลยีการปรับให้เหมาะสมแบบปรับได้นั้นมีความยืดหยุ่นอย่างมากในวิธีการของมัน

หมายเหตุ: การเปิดตัวการอัปเดต jdk6 10 (ดูที่หมายเหตุการเผยแพร่การปรับปรุง: การเปลี่ยนแปลงใน 1.6.0_10 ) พยายามปรับปรุงเวลาเริ่มต้น แต่ด้วยเหตุผลที่แตกต่างจากตัวเลือกฮอตสปอต


G. Demeckiชี้ให้เห็นในความคิดเห็นที่ใน JDK รุ่น 64 บิต-clientตัวเลือกจะถูกละเว้นเป็นเวลาหลายปี
ดูคำสั่งของWindowsjava :

-client

เลือก Java HotSpot Client VM
ปัจจุบัน JDK ที่มีความสามารถ 64 บิตจะไม่สนใจตัวเลือกนี้และใช้ Java Hotspot Server VMแทน


7
jdk6 อัพเดต 10 ขึ้นไปมีกระบวนการแบ็คกราวน์ที่ทำให้ไลบรารีรันไทม์ในหน่วยความจำยอมให้การเริ่มต้นเร็วขึ้นสำหรับกระบวนการใหม่มากกว่าที่จะต้องเพจทั้งหมดตามต้องการ
Thorbjørn Ravn Andersen

1
คิดว่า vm ไคลเอนต์ยัง inlined เลยเถิดดี
Thorbjørn Ravn Andersen

1
ฉันคิดว่าคำตอบนี้ควรได้รับการปรับปรุง เนื่องจากบน JDK เวอร์ชัน 64 บิต-clientตัวเลือกจะถูกละเว้นเป็นเวลาหลายปี
G. Demecki

@G.Demecki แน่นอน: คุณมีลิงค์เอกสารว่าตัวเลือกนี้ล้าสมัยหรือถูกเพิกเฉย?
VonC

1
แน่ใจ นี่คือdocumenationสำหรับ Java 7 สำหรับ Windows และตื่นตาตื่นใจข้อมูลที่คล้ายกันสามารถพบยังอยู่ใน Java 6 เอกสาร
G. Demecki

90

ความแตกต่างที่เห็นได้ชัดเจนที่สุดใน Java เวอร์ชันเก่าคือหน่วยความจำที่จัดสรรให้-clientกับ-serverแอปพลิเคชัน ตัวอย่างเช่นในระบบ Linux ของฉันฉันจะได้รับ:

$ java -XX:+PrintFlagsFinal -version 2>&1 | grep -i -E 'heapsize|permsize|version'
uintx AdaptivePermSizeWeight               = 20               {product}
uintx ErgoHeapSizeLimit                    = 0                {product}
uintx InitialHeapSize                     := 66328448         {product}
uintx LargePageHeapSizeThreshold           = 134217728        {product}
uintx MaxHeapSize                         := 1063256064       {product}
uintx MaxPermSize                          = 67108864         {pd product}
uintx PermSize                             = 16777216         {pd product}
java version "1.6.0_24"

ตามค่าเริ่มต้น-serverแต่ด้วย-clientตัวเลือกที่ฉันได้รับ:

$ java -client -XX:+PrintFlagsFinal -version 2>&1 | grep -i -E 'heapsize|permsize|version'
uintx AdaptivePermSizeWeight               = 20               {product}
uintx ErgoHeapSizeLimit                    = 0                {product}
uintx InitialHeapSize                     := 16777216         {product}
uintx LargePageHeapSizeThreshold           = 134217728        {product}
uintx MaxHeapSize                         := 268435456        {product}
uintx MaxPermSize                          = 67108864         {pd product}
uintx PermSize                             = 12582912         {pd product}
java version "1.6.0_24"

ดังนั้นด้วย-serverการ จำกัด หน่วยความจำส่วนใหญ่และการจัดสรรเริ่มต้นจะสูงกว่ามากสำหรับjavaรุ่นนี้

ค่าเหล่านี้สามารถเปลี่ยนแปลงได้สำหรับสถาปัตยกรรมระบบปฏิบัติการและเวอร์ชัน jvm ที่แตกต่างกัน เวอร์ชันล่าสุดของ jvm ได้ลบแฟล็กและย้ายความแตกต่างจำนวนมากระหว่างเซิร์ฟเวอร์และไคลเอ็นต์อีกครั้ง

จำเกินไปที่คุณสามารถดูรายละเอียดทั้งหมดของการทำงานโดยใช้jvm jvisualvmสิ่งนี้มีประโยชน์หากคุณมีผู้ใช้ที่หรือโมดูลที่ตั้งค่าJAVA_OPTSหรือใช้สคริปต์ที่เปลี่ยนตัวเลือกบรรทัดคำสั่ง สิ่งนี้จะช่วยให้คุณสามารถตรวจสอบการใช้พื้นที่ฮีปและเปอร์เรนแบบเรียลไทม์พร้อมสถิติอื่น ๆ อีกมากมาย


2
มันให้ตัวเลขที่เหมือนกันในโหมด -server และ -client สำหรับ java เวอร์ชัน "1.7.0_79" บนสภาพแวดล้อมรันไทม์ของ CentOS 7 [Java (TM) SE (สร้าง 1.7.0_79-b15) Java HotSpot (TM) 64-Bit Server VM ]
Basil Musa

4
นี่คือเหตุผลที่ฉันให้คำตอบ มันไม่เกี่ยวกับค่า แต่เป็นเรื่องเกี่ยวกับการเปิดใช้งานทุกคนทุกเวลาเพื่อค้นหาคำตอบสำหรับรุ่น jvm ที่เฉพาะเจาะจงของพวกเขา
Mark Booth

33

ระบบ -client และ -server เป็นไบนารีที่ต่างกัน พวกมันเป็นตัวแปลภาษาที่ต่างกันสองตัว (JITs) ที่เชื่อมต่อกับระบบรันไทม์เดียวกัน ระบบไคลเอนต์เหมาะสมที่สุดสำหรับแอปพลิเคชันที่ต้องการเวลาเริ่มต้นทำงานอย่างรวดเร็วหรือมีขนาดเล็กระบบเซิร์ฟเวอร์เหมาะสำหรับแอปพลิเคชันที่ประสิทธิภาพโดยรวมสำคัญที่สุด โดยทั่วไประบบไคลเอนต์จะเหมาะกว่าสำหรับแอปพลิเคชันแบบโต้ตอบเช่น GUI

ป้อนคำอธิบายรูปภาพที่นี่

เราเรียกใช้รหัสต่อไปนี้ด้วยสวิตช์ทั้งสอง:

package com.blogspot.sdoulger;

public class LoopTest {
    public LoopTest() {
        super();
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        spendTime();
        long end = System.currentTimeMillis();
        System.out.println("Time spent: "+ (end-start));

        LoopTest loopTest = new LoopTest();
    }

    private static void spendTime() {
        for (int i =500000000;i>0;i--) {
        }
    }
}

หมายเหตุ:รหัสได้รับการรวบรวมเพียงครั้งเดียว! ชั้นเรียนเหมือนกันทั้งสองวิ่ง!

ด้วย -client:
java.exe -client -classpath C: \ mywork \ classes com.blogspot.sdoulger.LoopTest
เวลาที่ใช้: 766

ด้วย -server:
java.exe -server -classpath C: \ mywork \ classes com.blogspot.sdoulger.LoopTest
เวลาที่ใช้: 0

ดูเหมือนว่าการเพิ่มประสิทธิภาพเชิงรุกของระบบเซิร์ฟเวอร์จะเป็นการลบลูปออกไปเพราะมันเข้าใจดีว่ามันไม่ได้ทำการกระทำใด ๆ !

การอ้างอิง


33

ข้อแตกต่างอย่างหนึ่งที่ฉันเพิ่งสังเกตเห็นคือในโหมด "ไคลเอนต์" ดูเหมือนว่า JVM จะให้หน่วยความจำที่ไม่ได้ใช้กลับไปยังระบบปฏิบัติการในขณะที่โหมด "เซิร์ฟเวอร์" เมื่อ JVM คว้าหน่วยความจำแล้วมันจะไม่ให้ กลับ. นั่นเป็นวิธีที่ปรากฏบน Solaris ด้วย Java6 ต่อไป (ใช้prstat -Zเพื่อดูจำนวนหน่วยความจำที่จัดสรรให้กับกระบวนการ)


22

เอกสารออนไลน์ของ Oracle ให้ข้อมูลบางอย่างสำหรับ Java SE 7

บนjava -หน้าตัวเรียกใช้งานแอปพลิเคชัน Javaสำหรับ Windows -clientตัวเลือกจะถูกละเว้นใน JDK 64 บิต:

เลือก Java HotSpot Client VM ปัจจุบัน jdk ที่มีความสามารถ 64 บิตจะไม่สนใจตัวเลือกนี้และใช้ Java HotSpot Server VM แทน

อย่างไรก็ตาม (เพื่อให้สิ่งที่น่าสนใจ) ภายใต้-serverมันระบุ:

เลือก Java HotSpot Server VM บน jdk ที่มีความสามารถ 64 บิตสนับสนุน Java HotSpot Server VM เท่านั้นดังนั้นตัวเลือก -server จึงเป็นนัย อาจมีการเปลี่ยนแปลงในการเปิดตัวในอนาคต

เซิร์ฟเวอร์-Class เครื่องตรวจจับหน้าให้ข้อมูลเกี่ยวกับที่ VM จะถูกเลือกโดย OS และสถาปัตยกรรม

ฉันไม่รู้ว่าสิ่งนี้มีผลกับ JDK 6 มากแค่ไหน


2
ขอบคุณฉันสงสัยว่าทำไมฉันไม่เห็นลูกค้า / jvm.dll บน JDK7
Archimedes Trajano

16

จาก Goetz - Java Concurrency ในทางปฏิบัติ:

  1. แก้จุดบกพร่องเคล็ดลับ: สำหรับการใช้งานเซิร์ฟเวอร์ให้แน่ใจว่าได้เสมอระบุ-serverสวิตช์บรรทัดคำสั่ง JVM เมื่ออัญเชิญ JVM, แม้กระทั่งสำหรับการพัฒนาและการทดสอบ เซิร์ฟเวอร์ JVM ดำเนินการปรับให้เหมาะสมมากกว่า JVM ไคลเอ็นต์เช่นตัวแปร hoisting จากลูปที่ไม่ได้แก้ไขในลูป รหัสที่อาจปรากฏขึ้นเพื่อทำงานในสภาพแวดล้อมการพัฒนา (ไคลเอนต์ JVM) สามารถแบ่งในสภาพแวดล้อมการปรับใช้ (เซิร์ฟเวอร์ JVM) ตัวอย่างเช่นมีเรา“ลืม” ที่จะประกาศตัวแปรหลับเป็นสารระเหยในรายชื่อ 3.4, เซิร์ฟเวอร์ JVM จะยกการทดสอบออกมาจากวง (เปลี่ยนมันเป็นห่วงอนันต์) แต่ลูกค้า JVM จะไม่ การวนรอบไม่สิ้นสุดที่แสดงในการพัฒนานั้นมีค่าใช้จ่ายน้อยกว่าการแสดงในการผลิต

รายชื่อ 3.4 การนับแกะ

volatile boolean asleep; ... while (!asleep) countSomeSheep();

ความสำคัญของฉัน YMMV


15

IIRC เซิร์ฟเวอร์ VM ทำการเพิ่มประสิทธิภาพฮอตสปอตมากขึ้นเมื่อเริ่มต้นจึงทำงานได้เร็วขึ้น แต่ใช้เวลาเริ่มต้นนานขึ้นและใช้หน่วยความจำเพิ่มขึ้นเล็กน้อย VM ไคลเอนต์ defers ของการปรับให้เหมาะสมที่สุดเพื่อให้เริ่มต้นเร็วขึ้น

แก้ไขเพื่อเพิ่ม: นี่คือข้อมูลบางอย่างจากซันมันไม่ได้เฉพาะเจาะจงมากนัก แต่จะให้แนวคิดบางอย่างแก่คุณ


5

IIRC มันเกี่ยวข้องกับกลยุทธ์การเก็บขยะ ทฤษฎีคือไคลเอนต์และเซิร์ฟเวอร์จะแตกต่างกันในแง่ของวัตถุที่มีอายุสั้นซึ่งเป็นสิ่งสำคัญสำหรับอัลกอริทึม GC ที่ทันสมัย

นี่คือลิงค์ในโหมดเซิร์ฟเวอร์ อนิจจาพวกเขาไม่ได้พูดถึงโหมดไคลเอนต์

นี่คือลิงค์ทั่ว ๆไปของ GC โดยทั่วไป นี้เป็นบทความพื้นฐานเพิ่มเติม ไม่แน่ใจว่าที่อยู่ - เซิร์ฟเวอร์ vs - ไคลเอนต์ แต่นี่เป็นเนื้อหาที่เกี่ยวข้อง

ที่ No Fluff Just Stuff ทั้ง Ken Sipe และ Glenn Vandenburg พูดถึงเรื่องนี้ได้อย่างยอดเยี่ยม


3

ฉันไม่ได้สังเกตเห็นความแตกต่างของเวลาเริ่มต้นระหว่าง 2 แต่มีการปรับปรุงประสิทธิภาพการทำงานของแอปพลิเคชั่นน้อยมากด้วย "-server" (เซิร์ฟเวอร์ Solaris ทุกคนที่ใช้ SunRays เพื่อเรียกใช้แอป) ต่ำกว่า 1.5


6
ขึ้นอยู่กับว่าโปรแกรมของคุณกำลังทำอะไร สำหรับแอปพลิเคชั่นที่ใช้ตัวประมวลผลจำนวนมากที่ทำสิ่งเดียวกันซ้ำ ๆ กันฉันได้สังเกตเห็นการปรับปรุงขนาดใหญ่ (มากถึง 10 เท่า) ด้วย -server
Dan Dyer

1
แดนคุณมีการอ้างอิงถึงสิ่งนี้หรือไม่? ฉันต้องการตรวจสอบเพิ่มเติม
Thorbjørn Ravn Andersen

1
การรัน Sunflow ด้วยเซิร์ฟเวอร์ VM นั้นเร็วกว่าไคลเอนต์ sunflow.sourceforge.net
John

1

ครั้งล่าสุดที่ฉันได้ดูเรื่องนี้ (และยอมรับว่ามันเป็นเวลาครู่หนึ่ง) ความแตกต่างที่ใหญ่ที่สุดที่ฉันสังเกตเห็นคือในการเก็บขยะ

IIRC:

  • เซิร์ฟเวอร์ฮีพ VM มีจำนวนรุ่นที่ต่างกันกว่า Client VM และอัลกอริทึมการรวบรวมขยะที่แตกต่างกัน สิ่งนี้อาจไม่เป็นจริงอีกต่อไป
  • เซิร์ฟเวอร์ VM จะจัดสรรหน่วยความจำและไม่ปล่อยไปยังระบบปฏิบัติการ
  • VM เซิร์ฟเวอร์จะใช้อัลกอริธึมการปรับให้เหมาะสมที่ซับซ้อนยิ่งขึ้นดังนั้นจึงมีข้อกำหนดด้านเวลาและหน่วยความจำที่มากขึ้นสำหรับการปรับให้เหมาะสมที่สุด

หากคุณสามารถเปรียบเทียบสอง java VMs หนึ่งไคลเอ็นต์หนึ่งเซิร์ฟเวอร์โดยใช้เครื่องมือjvisualvmคุณควรเห็นความแตกต่างของความถี่และผลกระทบของการรวบรวมขยะรวมถึงจำนวนรุ่น

ฉันมีภาพหน้าจอที่แสดงความแตกต่างได้ดี แต่ฉันไม่สามารถทำซ้ำได้เนื่องจากฉันมี JVM 64 บิตซึ่งใช้เซิร์ฟเวอร์ VM เท่านั้น (และฉันก็ไม่ต้องกังวลที่จะดาวน์โหลดและบิดรุ่น 32 บิตในระบบของฉันด้วย)

ดูเหมือนจะไม่เป็นเช่นนั้นอีกต่อไปหลังจากลองใช้งานโค้ดบางอย่างบน windows พร้อมทั้งเซิร์ฟเวอร์และไคลเอนต์ VMs ฉันดูเหมือนจะได้รับโมเดลรุ่นเดียวกันสำหรับทั้งสอง ...


1

เมื่อทำการโอนย้ายจากรุ่น 1.4 ถึง 1.7 ("1.7.0_55") สิ่งที่เราสังเกตเห็นที่นี่คือไม่มีความแตกต่างดังกล่าวในค่าเริ่มต้นที่กำหนดให้กับ heapsize | permsize | ThreadStackSize พารามิเตอร์ในโหมดไคลเอ็นต์และเซิร์ฟเวอร์

โดยวิธีการ ( http://www.oracle.com/technetwork/java/ergo5-140223.html ) นี่คือตัวอย่างข้อมูลที่นำมาจากลิงก์ด้านบน

initial heap size of 1/64 of physical memory up to 1Gbyte
maximum heap size of ¼ of physical memory up to 1Gbyte

ThreadStackSize สูงกว่า 1.7 ในขณะที่ผ่านฟอรัม Open JDK มีการพูดคุยกันว่าขนาดเฟรมที่ระบุนั้นค่อนข้างสูงกว่าในเวอร์ชั่น 1.7 เชื่อว่าความแตกต่างที่แท้จริงอาจเป็นไปได้ในการวัด ณ รันไทม์ตามพฤติกรรมของแอปพลิเคชันของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.